Mengelola video selama panggilan

Pelajari cara mengelola panggilan dengan Azure Communication Services SDKS. Kami akan belajar bagaimana mengelola menerima dan mengirim video dalam panggilan.

Prasyarat

Pasang SDK

npm install Gunakan perintah untuk menginstal SDK Umum dan Panggilan Azure Communication Services untuk JavaScript:

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

Menginisialisasi objek yang diperlukan

Instans CallClient diperlukan untuk sebagian besar operasi panggilan. Saat membuat instans baru CallClient , Anda dapat mengonfigurasinya dengan opsi kustom seperti Logger instans.

CallClient Dengan instans, Anda dapat membuat CallAgent instans dengan memanggil createCallAgent. Metode ini secara asinkron mengembalikan objek instans CallAgent.

Metode createCallAgent menggunakan CommunicationTokenCredential sebagai argumen. Hal ini menerima token akses pengguna.

Anda dapat menggunakan metode getDeviceManager pada instans CallClient untuk mengakses deviceManager.

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

Cara terbaik mengelola konektivitas SDK ke infrastruktur Microsoft

Call Agent Instans ini membantu Anda mengelola panggilan (untuk bergabung atau memulai panggilan). Untuk bekerja, SDK panggilan Anda perlu tersambung ke infrastruktur Microsoft untuk mendapatkan pemberitahuan panggilan masuk dan mengoordinasikan detail panggilan lainnya. Anda Call Agent memiliki dua kemungkinan status:

Koneksi - Nilai Call AgentConnected connectionStatue berarti SDK klien terhubung dan mampu menerima pemberitahuan dari infrastruktur Microsoft.

Terputus - Nilai Call AgentDisconnected status connectionStatue ada masalah yang mencegah SDK menyambungkannya dengan benar. Call Agent harus dibuat ulang.

  • invalidToken: Jika token kedaluwarsa atau instans tidak valid Call Agent terputus dengan kesalahan ini.
  • connectionIssue: Jika ada masalah dengan klien yang terhubung ke microsoft infrascture, setelah banyak percobaan Call Agent ulang mengekspos connectionIssue kesalahan.

Anda dapat memeriksa apakah lokal Call Agent Anda tersambung ke infrastruktur Microsoft dengan memeriksa nilai connectionState properti saat ini. Selama panggilan aktif, Anda dapat mendengarkan connectionStateChanged acara untuk menentukan apakah Call Agent perubahan dari Koneksi ke status Terputus.

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

Manajemen perangkat

Untuk mulai menggunakan video dengan SDK Panggilan, Anda harus dapat mengelola perangkat. Perangkat memungkinkan Anda untuk mengontrol apa yang mentransmisikan Audio dan Video ke panggilan.

deviceManagerDengan , Anda dapat menghitung perangkat lokal yang dapat mengirimkan aliran audio dan video Anda dalam panggilan. Anda juga dapat menggunakan deviceManager untuk meminta izin untuk mengakses mikrofon dan kamera perangkat lokal.

Anda dapat mengakses deviceManager dengan memanggil metode callClient.getDeviceManager():

const deviceManager = await callClient.getDeviceManager();

Mendapatkan perangkat lokal

Untuk mengakses perangkat lokal, Anda dapat menggunakan deviceManager metode getCameras() enumerasi dan getMicrophones. Metode tersebut adalah tindakan asinkron.

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

Mengatur perangkat default

Setelah mengetahui perangkat apa yang tersedia untuk digunakan, Anda dapat mengatur perangkat default untuk mikrofon, speaker, dan kamera. Jika default klien tidak diatur, SDK Communication Services menggunakan default sistem operasi.

Mikrofon

Mengakses perangkat yang digunakan

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

Mengatur perangkat yang akan digunakan

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

Speaker

Mengakses perangkat yang digunakan

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

Mengatur perangkat yang akan digunakan

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

Kamera

Mengakses perangkat yang digunakan

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

Mengatur perangkat yang akan digunakan

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

Masing-masing CallAgent dapat memilih mikrofon dan speakernya sendiri pada yang terkait DeviceManager. Sebaiknya gunakan CallAgents mikrofon dan speaker yang berbeda. Mereka tidak boleh berbagi mikrofon atau speaker yang sama. Jika berbagi terjadi, maka Diagnostik Yang Dihadapi Pengguna Mikrofon mungkin dipicu dan mikrofon berhenti berfungsi tergantung pada browser / os.

Aliran video lokal

Agar dapat mengirim video dalam panggilan, Anda perlu membuat LocalVideoStreamobjek.

const localVideoStream = new LocalVideoStream(camera);

Kamera yang diteruskan sebagai parameter adalah salah satu objek yang VideoDeviceInfo dikembalikan oleh deviceManager.getCameras()metode .

A LocalVideoStream memiliki properti berikut:

  • source: Informasi perangkat.
const source = localVideoStream.source;
  • mediaStreamType: Dapat berupa Video, ScreenSharing, atau RawMedia.
const type: MediaStreamType = localVideoStream.mediaStreamType;

Pratinjau kamera lokal

Anda dapat menggunakan deviceManager dan VideoStreamRenderer untuk mulai merender aliran dari kamera lokal Anda. LocalVideoStream Setelah dibuat, gunakan untuk menyiapkannyaVideoStreamRenderer. VideoStreamRendererSetelah dibuat, panggil metodenya createView() untuk mendapatkan tampilan yang dapat Anda tambahkan sebagai anak ke halaman Anda.

Aliran ini tidak dikirim ke peserta lain; ini adalah umpan pratinjau lokal.

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

Menghentikan pratinjau lokal

Untuk menghentikan panggilan pratinjau lokal, buang tampilan yang berasal dari VideoStreamRenderer. Setelah VideoStreamRenderer dibuang, hapus tampilan dari pohon html dengan memanggil removeChild() metode dari Simpul DOM yang berisi pratinjau Anda.

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

Minta izin ke kamera dan mikrofon

Aplikasi tidak dapat menggunakan kamera atau mikrofon tanpa izin. Anda dapat menggunakan deviceManager untuk meminta pengguna memberikan izin kamera dan/atau mikrofon:

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

Setelah janji diselesaikan, metode akan kembali dengan DeviceAccess objek yang menunjukkan apakah audio dan video izin diberikan:

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

Catatan

  • videoDevicesUpdated peristiwa diaktifkan saat perangkat video dicolokkan/dilepas.
  • audioDevicesUpdated peristiwa diaktifkan saat perangkat audio dicolokkan.
  • Saat DeviceManager dibuat, pada awalnya perangkat tidak tahu tentang perangkat apa pun jika izin belum diberikan, jadi awalnya nama perangkatnya kosong dan tidak berisi informasi perangkat terperinci. Jika kita kemudian memanggil API DeviceManager.askPermission(), pengguna akan dimintai akses perangkat. Saat pengguna memilih 'izinkan' untuk memberikan akses yang dipelajari manajer perangkat tentang perangkat pada sistem, perbarui daftar perangkat dan keluarkan peristiwa 'audioDevicesUpdated' dan 'videoDevicesUpdated'. Jika pengguna me-refresh halaman dan membuat manajer perangkat, manajer perangkat dapat mempelajari tentang perangkat karena pengguna memberikan akses sebelumnya. Ini memiliki daftar perangkat yang diisi awalnya dan tidak memancarkan peristiwa 'audioDevicesUpdated' atau 'videoDevicesUpdated'.
  • Enumerasi/pilihan pembicara tidak didukung di Android Chrome, iOS Safari, atau macOS Safari.

Melakukan panggilan dengan kamera video

Penting

Saat ini hanya satu aliran video lokal keluar yang didukung.

Untuk melakukan panggilan video, Anda harus menghitung kamera lokal dengan menggunakan metode getCameras() di deviceManager.

Setelah Anda memilih kamera, gunakan untuk membuat instans LocalVideoStream. Teruskan dalam videoOptions sebagai item dalam larik localVideoStream ke metode CallAgentstartCall.

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);
  • Anda juga dapat bergabung dengan panggilan dengan video dengan CallAgent.join() API, dan menerima dan memanggil dengan video dengan Call.Accept() API.
  • Saat panggilan Anda tersambung, secara otomatis mulai mengirim aliran video dari kamera yang dipilih ke peserta lain.

Mulai dan berhenti mengirim video lokal saat melakukan panggilan

Mulai video

Untuk memulai video saat melakukan panggilan, Anda harus menghitung kamera menggunakan getCameras metode pada deviceManager objek. Kemudian buat instans baru LocalVideoStream dengan kamera yang diinginkan, lalu teruskan objek LocalVideoStream ke dalam metode startVideo:

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

Hentikan Video

Setelah Anda berhasil mulai mengirim video, LocalVideoStream instans jenis Video ditambahkan ke localVideoStreams koleksi pada instans panggilan.

Menemukan aliran video di objek Panggilan

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

Hentikan video lokal Untuk menghentikan video lokal saat melakukan panggilan, teruskan localVideoStream instans yang sedang digunakan untuk video ke metode stopVideo dari Call:

await call.stopVideo(localVideoStream);

Anda dapat beralih ke perangkat kamera yang berbeda saat memiliki LocalVideoStream aktif dengan memanggil switchSource instans tersebut LocalVideoStream :

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

Jika perangkat video yang ditentukan tidak tersedia:

  • Saat dalam panggilan, jika video Anda nonaktif dan Anda memulai video menggunakan call.startVideo(), metode ini melempar SourceUnavailableError diagnostik dan cameraStartFailed pengguna yang menghadap diatur ke true.
  • Panggilan ke localVideoStream.switchSource() metode menyebabkan cameraStartFailed diatur ke true. Panduan Diagnostik Panggilan kami memberikan informasi tambahan tentang cara mendiagnosis masalah terkait panggilan.

Untuk memverifikasi apakah video lokal aktif atau nonaktif, Anda dapat menggunakan Call metode isLocalVideoStarted, yang mengembalikan true atau false:

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

Untuk mendengarkan perubahan pada video lokal, Anda dapat berlangganan dan berhenti berlangganan peristiwa isLocalVideoStartedChanged:

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

Memulai dan menghentikan berbagi layar saat melakukan panggilan

Untuk memulai berbagi layar saat melakukan panggilan, Anda dapat menggunakan metode startScreenSharing() asinkron pada Call objek:

Mulai berbagi layar

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

Temukan berbagi layar dalam koleksi LocalVideoStream

Setelah Anda berhasil mulai mengirim berbagi layar, LocalVideoStream instans jenis ScreenSharing, ditambahkan ke localVideoStreams koleksi pada instans panggilan.

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

Hentikan berbagi layar

Untuk menghentikan berbagi layar saat melakukan panggilan, Anda dapat menggunakan stoptScreenSharing API asinkron:

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

Periksa status berbagi layar

Untuk memverifikasi apakah berbagi layar aktif atau nonaktif, Anda dapat menggunakan ISScreenSharingOn API, yang mengembalikan true atau false:

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

Untuk mendengarkan perubahan pada berbagi layar, Anda dapat berlangganan dan berhenti berlangganan peristiwa isScreenSharingOnChanged:

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

Penting

Fitur Azure Communication Services ini saat ini dalam pratinjau.

API pratinjau dan SDK disediakan tanpa perjanjian tingkat layanan. Kami menyarankan agar Anda tidak menggunakannya untuk beban kerja produksi. Beberapa fitur mungkin tidak didukung, atau mungkin memiliki kemampuan yang dibatasi.

Untuk informasi selengkapnya, tinjau Ketentuan Penggunaan Tambahan untuk Pratinjau Microsoft Azure.

Pratinjau berbagi layar lokal berada dalam pratinjau publik dan tersedia sebagai bagian dari versi 1.15.1-beta.1+.

Pratinjau berbagi layar lokal

Anda dapat menggunakan VideoStreamRenderer untuk mulai merender aliran dari berbagi layar lokal sehingga Anda dapat melihat apa yang Anda kirim sebagai aliran berbagi layar.

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

Merender streaming video/screensharing peserta jarak jauh

Untuk merender video peserta jarak jauh atau berbagi layar, langkah pertama adalah mendapatkan referensi pada RemoteVideoStream yang ingin Anda render. Ini dapat dilakukan dengan melalui array atau aliran video (videoStreams) dari RemoteParticipant. Koleksi peserta jarak jauh diakses melalui Call objek .

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

Untuk merender RemoteVideoStream, Anda harus berlangganan acaranya isAvailableChanged . isAvailable Jika properti berubah menjadi true, peserta jarak jauh mengirim aliran video. Setelah itu terjadi, buat instans baru VideoStreamRenderer, lalu buat instans VideoStreamRendererView baru dengan menggunakan metode createView asinkron.
Anda kemudian dapat melampirkan view.target ke elemen UI mana pun.

Setiap kali ketersediaan aliran jarak jauh berubah, Anda dapat menghancurkan seluruh VideoStreamRenderer atau tertentu VideoStreamRendererView. Jika Anda memutuskan untuk menyimpannya, tampilan akan menampilkan bingkai video kosong.

// 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 untuk menata spinner pemuatan melalui aliran video jarak jauh.

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

Kualitas video jarak jauh

Azure Communication Services WebJS SDK, menyediakan fitur yang disebut Optimal Video Count (OVC), mulai versi 1.15.1. Fitur ini dapat digunakan untuk menginformasikan aplikasi pada run-time tentang berapa banyak video masuk dari peserta yang berbeda dapat dirender secara optimal pada saat tertentu dalam panggilan grup (2+ peserta). Fitur ini mengekspos properti optimalVideoCount yang berubah secara dinamis selama panggilan berdasarkan kemampuan jaringan dan perangkat keras dari titik akhir lokal. Nilai optimalVideoCount detail berapa banyak video dari aplikasi peserta yang berbeda harus dirender pada saat tertentu. Aplikasi harus menangani perubahan ini dan memperbarui jumlah video yang dirender sesuai dengan rekomendasi. Ada periode debounce (sekitar 10 d) di antara setiap pembaruan.

Penggunaan Fitur optimalVideoCount ini adalah fitur panggilan. Anda perlu mereferensikan fitur OptimalVideoCount melalui feature metode Call objek. Anda kemudian dapat mengatur pendengar melalui on metode OptimalVideoCountCallFeature yang akan diberi tahu saat optimalVideoCount berubah. Untuk berhenti berlangganan dari perubahan, Anda dapat memanggil off metode .

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

Contoh penggunaan: Aplikasi harus berlangganan perubahan Jumlah Video Optimal dalam panggilan grup. Perubahan jumlah video optimal dapat ditangani dengan membuat perender baru (createView metode) atau membuang tampilan (dispose) dan memperbarui tata letak aplikasi yang sesuai.

Properti aliran video jarak jauh

Aliran video jarak jauh memiliki properti berikut:

const id: number = remoteVideoStream.id;
  • id: ID aliran video jarak jauh.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
  • mediaStreamType: Dapat berupa Video atau ScreenSharing.
const isAvailable: boolean = remoteVideoStream.isAvailable;
  • isAvailable: Menentukan apakah titik akhir peserta jarak jauh secara aktif mengirim aliran.
const isReceiving: boolean = remoteVideoStream.isReceiving;
  • isReceiving:
    • Menginformasikan aplikasi jika data aliran video jarak jauh diterima atau tidak.

    • Bendera berpindah ke false dalam skenario berikut:

      • Peserta jarak jauh yang berada di browser seluler membawa aplikasi browser ke latar belakang.
      • Peserta jarak jauh atau pengguna yang menerima video memiliki masalah jaringan yang memengaruhi kualitas video secara drastis.
      • Peserta jarak jauh yang berada di macOS/iOS Safari memilih "Jeda" dari bilah alamat mereka.
      • Peserta jarak jauh mengalami pemutusan jaringan.
      • Peserta jarak jauh di ponsel membunuh atau mengakhiri browser.
      • Peserta jarak jauh di ponsel atau desktop mengunci perangkatnya. Skenario ini juga berlaku jika peserta jarak jauh berada di komputer desktop dan tidur.
    • Bendera berpindah ke true dalam skenario berikut:

      • Peserta jarak jauh yang berada di browser seluler dan memiliki latar belakang browsernya membawanya kembali ke latar depan.
      • Peserta jarak jauh yang berada di macOS/iOS Safari memilih "Lanjutkan" dari bilah alamat mereka setelah menjeda videonya.
      • Peserta jarak jauh tersambung kembali ke jaringan setelah pemutusan sambungan sementara.
      • Peserta jarak jauh di ponsel membuka kunci perangkatnya dan kembali ke panggilan di browser selulernya.
    • Fitur ini meningkatkan pengalaman pengguna untuk merender aliran video jarak jauh.

    • Anda dapat menampilkan spinner pemuatan melalui aliran video jarak jauh saat bendera isReceiving berubah menjadi false. Anda tidak perlu menerapkan pemuatan spinner, tetapi spinner pemuatan adalah penggunaan paling umum untuk pengalaman pengguna yang lebih baik.

const size: StreamSize = remoteVideoStream.size;
  • size: Ukuran streaming dengan informasi tentang lebar dan tinggi video.

Metode dan properti VideoStreamRenderer

await videoStreamRenderer.createView();

Buat VideoStreamRendererView instans yang dapat dilampirkan di antarmuka pengguna aplikasi untuk merender aliran video jarak jauh, menggunakan metode asinkron createView() , itu menyelesaikan ketika aliran siap untuk merender dan mengembalikan objek dengan target properti yang mewakili video elemen yang dapat dimasukkan di mana saja di pohon DOM.

videoStreamRenderer.dispose();

Buang videoStreamRenderer dan semua instans terkait VideoStreamRendererView .

Metode dan properti VideoStreamRendererView

Saat membuat VideoStreamRendererView, Anda dapat menentukan properti scalingMode dan isMirrored. scalingMode dapat berupa Stretch, Crop, atau Fit. Jika isMirrored ditentukan, aliran yang dirender dibalik secara vertikal.

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

Setiap instans VideoStreamRendererView memiliki properti target yang mewakili permukaan penyajian. Melampirkan properti ini di UI aplikasi:

htmlElement.appendChild(view.target);

Anda dapat memperbarui scalingMode dengan menerapkan metode updateScalingMode:

view.updateScalingMode('Crop');

Kirim streaming video dari dua kamera berbeda, dalam panggilan yang sama dari perangkat desktop yang sama.

Penting

Fitur Azure Communication Services ini saat ini dalam pratinjau.

API pratinjau dan SDK disediakan tanpa perjanjian tingkat layanan. Kami menyarankan agar Anda tidak menggunakannya untuk beban kerja produksi. Beberapa fitur mungkin tidak didukung, atau mungkin memiliki kemampuan yang dibatasi.

Untuk informasi selengkapnya, tinjau Ketentuan Penggunaan Tambahan untuk Pratinjau Microsoft Azure.

Kirim aliran video dari dua kamera berbeda dalam panggilan yang sama didukung sebagai bagian dari versi 1.17.1-beta.1+ pada browser yang didukung desktop.

  • Anda dapat mengirim aliran video dari dua kamera berbeda dari satu tab/aplikasi browser desktop, dalam panggilan yang sama, dengan cuplikan kode berikut:
// 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();

Batasan:

  • Ini harus dilakukan dengan dua instans yang berbeda CallAgent menggunakan identitas yang berbeda. Cuplikan kode menunjukkan dua agen panggilan yang digunakan, masing-masing dengan objek Panggilannya sendiri.
  • Dalam contoh kode, kedua CallAgents bergabung dengan panggilan yang sama (ID panggilan yang sama). Anda juga dapat menggabungkan panggilan yang berbeda dengan setiap agen dan mengirim satu video dalam satu panggilan dan video yang berbeda di panggilan lain.
  • Mengirim kamera yang sama di kedua CallAgent, tidak didukung. Mereka harus dua kamera yang berbeda.
  • Mengirim dua kamera berbeda dengan satu CallAgent saat ini tidak didukung.
  • Di macOS Safari, efek video kabur latar belakang (dari @azure/communication-effects), hanya dapat diterapkan ke satu kamera, dan bukan keduanya secara bersamaan.

Pasang SDK

Temukan file build.gradle tingkat proyek Anda dan tambahkan mavenCentral() ke daftar repositori di bawah buildscript dan allprojects:

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

Kemudian, di file build.gradle tingkat modul Anda, tambahkan baris berikut ke bagian dependencies :

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

Menginisialisasi objek yang diperlukan

Untuk membuat CallAgent instans, Anda harus memanggil createCallAgent metode pada CallClient instans. Panggilan ini secara asinkron mengembalikan objek instans CallAgent .

Metode ini createCallAgent mengambil CommunicationUserCredential sebagai argumen, yang merangkum token akses.

Untuk mengakses DeviceManager, Anda harus membuat instans terlebih callAgent dahulu. Kemudian Anda dapat menggunakan CallClient.getDeviceManager metode untuk mendapatkan 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();

Untuk mengatur nama tampilan penelepon, gunakan metode alternatif ini:

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

Manajemen perangkat

Untuk mulai menggunakan video dengan Panggilan, Anda perlu mengetahui cara mengelola perangkat. Perangkat memungkinkan Anda untuk mengontrol apa yang mentransmisikan Audio dan Video ke panggilan.

DeviceManager memungkinkan Anda menghitung perangkat lokal yang dapat digunakan dalam panggilan untuk mengirimkan aliran audio/video Anda. Hal ini juga memungkinkan Anda untuk meminta izin dari pengguna untuk mengakses mikrofon dan kamera mereka menggunakan API browser asli.

Anda dapat mengakses deviceManager dengan memanggil callClient.getDeviceManager() metode.

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

Hitung perangkat lokal

Untuk mengakses perangkat lokal, Anda dapat menggunakan metode enumerasi di Manajer Perangkat. Enumerasi adalah tindakan sinkron.

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

Pratinjau kamera lokal

Anda dapat menggunakan DeviceManager dan Renderer untuk mulai merender aliran dari kamera lokal Anda. Aliran ini tidak akan dikirim ke peserta lain; ini adalah umpan pratinjau lokal. Ini adalah tindakan asinkron.

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

Lakukan panggilan 1:1 dengan kamera video

Peringatan

Saat ini hanya satu aliran video lokal keluar yang didukung Untuk melakukan panggilan dengan video, Anda harus menghitung kamera lokal menggunakan deviceManagergetCameras API. Setelah Anda memilih kamera yang diinginkan, gunakan untuk membuat instans LocalVideoStream dan teruskan ke videoOptions sebagai item dalam array localVideoStream ke metode call. Setelah panggilan tersambung, panggilan akan secara otomatis mulai mengirim stream video dari kamera yang dipilih ke peserta lain.

Catatan

Karena masalah privasi, video tidak akan dibagikan ke panggilan jika tidak dipratinjau secara lokal. Lihat Pratinjau kamera lokal untuk detail selengkapnya.

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

Mulai dan berhenti mengirim video lokal

Untuk memulai video, Anda harus menghitung kamera menggunakan getCameraList API pada deviceManager objek. Kemudian buat instans baru LocalVideoStream yang meneruskan kamera yang diinginkan, dan meneruskannya di API startVideo sebagai argumen:

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

Setelah Anda berhasil memulai mengirim video, instans LocalVideoStream akan ditambahkan ke localVideoStreams koleksi pada instans panggilan.

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

Untuk menghentikan video lokal, teruskan LocalVideoStream instans yang tersedia dalam localVideoStreams koleksi:

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

Anda dapat beralih ke perangkat kamera lain saat video dikirim dengan memanggil switchSource di LocalVideoStreaminstans:

currentLocalVideoStream.switchSource(source).get();

Merender aliran video peserta jarak jauh

Untuk mencantumkan aliran video dan aliran berbagi layar peserta jarak jauh, periksa videoStreams koleksi:

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

Untuk merender RemoteVideoStream dari peserta jarak jauh, Anda harus berlangganan ke OnVideoStreamsUpdated peristiwa.

Dalam peristiwa tersebut, perubahan isAvailable properti menjadi true menunjukkan bahwa peserta jarak jauh saat ini sedang mengirim aliran. Setelah itu terjadi, buat instans baru Renderer, lalu buat baru RendererView menggunakan API createView asinkron baru dan lampirkan view.target di mana saja di UI aplikasi Anda.

Setiap kali ketersediaan perubahan aliran jarak jauh, Anda dapat memilih untuk menghancurkan seluruh Renderer, tertentu RendererView atau menyimpannya, tetapi hal ini akan mengakibatkan menampilkan bingkai video kosong.

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

Properti aliran video jarak jauh

Aliran video jarak jauh memiliki beberapa properti

  • Id - ID aliran video jarak jauh
int id = remoteVideoStream.getId();
  • MediaStreamType - Bisa berupa 'Video' atau 'ScreenSharing'
MediaStreamType type = remoteVideoStream.getMediaStreamType();
  • isAvailable - Menunjukkan apakah titik akhir peserta jarak jauh secara aktif mengirim aliran
boolean availability = remoteVideoStream.isAvailable();

Metode dan properti renderer

Objek render mengikuti API

  • Buat VideoStreamRendererView instans yang nantinya dapat dilampirkan di UI aplikasi untuk merender aliran video jarak jauh.
// Create a view for a video stream
VideoStreamRendererView.createView()
  • Buang perender dan semua VideoStreamRendererView yang terkait dengan perender ini. Untuk dipanggil ketika Anda telah menghapus semua tampilan terkait dari UI.
VideoStreamRenderer.dispose()
  • StreamSize - ukuran (lebar/tinggi) aliran video jarak jauh
StreamSize renderStreamSize = VideoStreamRenderer.getSize();
int width = renderStreamSize.getWidth();
int height = renderStreamSize.getHeight();

Metode dan properti RendererView

Saat membuat VideoStreamRendererView, Anda dapat menentukan properti ScalingMode dan mirrored yang akan berlaku untuk tampilan ini: Mode penskalaan dapat berupa salah satu dari 'CROP' | 'FIT'

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

RendererView yang dibuat kemudian dapat dilampirkan ke UI aplikasi menggunakan cuplikan berikut:

layout.addView(rendererView);

Anda nantinya dapat memperbarui mode penskalaan dengan memanggil API updateScalingMode pada objek RendererView dengan salah satu ScalingMode.CROP | ScalingMode.FIT sebagai argumen.

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

Siapkan sistem Anda

Buat proyek Xcode

Di Xcode, buat proyek iOS baru dan pilih templat Aplikasi Tampilan Tunggal. Mulai cepat ini menggunakan kerangka kerja SwiftUI, jadi Anda harus mengatur Bahasa ke Swift dan mengatur Antarmuka ke SwiftUI.

Anda tidak akan membuat pengujian selama mulai cepat ini. Jangan ragu untuk menghapus kotak centang Sertakan Pengujian.

Cuplikan layar yang memperlihatkan jendela untuk membuat proyek dalam Xcode.

Pasang paket dan dependensi dengan menggunakan CocoaPods

  1. Buat Podfile untuk aplikasi Anda, seperti contoh ini:

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

  3. Buka .xcworkspace dengan menggunakan Xcode.

Minta akses ke mikrofon

Untuk mengakses mikrofon perangkat, Anda perlu memperbarui daftar properti informasi aplikasi dengan menggunakan NSMicrophoneUsageDescription. Anda mengatur nilai terkait ke string yang akan disertakan dalam dialog yang digunakan sistem untuk meminta akses dari pengguna.

Klik kanan entri Info.plist dari pohon proyek, lalu pilih Buka Sebagai>Kode Sumber. Tambahkan baris berikut ke bagian <dict> tingkat atas, lalu simpan file.

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

Menyiapkan kerangka kerja aplikasi

Buka file ContentView.swift proyek Anda. import Tambahkan deklarasi ke bagian atas file untuk mengimpor AzureCommunicationCalling pustaka. Selain itu, impor AVFoundation. Anda akan memerlukannya untuk permintaan izin audio dalam kode.

import AzureCommunicationCalling
import AVFoundation

Inisialisasi CallAgent

Untuk membuat CallAgent instans dari CallClient, Anda harus menggunakan callClient.createCallAgent metode yang secara asinkron mengembalikan CallAgent objek setelah diinisialisasi.

Untuk membuat klien panggilan, berikan CommunicationTokenCredential objek:

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

Teruskan CommunicationTokenCredential objek yang Anda buat ke CallClient, dan atur nama tampilan:

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

Kelola perangkat

Untuk mulai menggunakan video dengan Panggilan, Anda perlu mengetahui cara mengelola perangkat. Perangkat memungkinkan Anda untuk mengontrol apa yang mentransmisikan Audio dan Video ke panggilan.

DeviceManager memungkinkan Anda menghitung perangkat lokal yang dapat digunakan dalam panggilan untuk mengirimkan aliran audio/video Anda. Pengelolaan perangkat juga memungkinkan Anda untuk meminta izin akses dari pengguna untuk mengakses mikrofon atau kamera. Anda dapat mengakses deviceManager pada callClient objek.

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

Hitung perangkat lokal

Untuk mengakses perangkat lokal, Anda dapat menggunakan metode enumerasi di manajer perangkat. Enumerasi adalah tindakan sinkron.

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

Dapatkan pratinjau kamera lokal

Anda dapat menggunakannya Renderer untuk mulai merender aliran dari kamera lokal Anda. Aliran ini tidak akan dikirim ke peserta lain; ini adalah umpan pratinjau lokal. Ini adalah tindakan asinkron.

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

Dapatkan properti pratinjau kamera lokal

Penyaji memiliki sekumpulan properti dan metode yang memungkinkan Anda mengontrol penyajian.

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

Lakukan panggilan 1:1 dengan video

Untuk mendapatkan instans manajer perangkat, lihat bagian tentang mengelola perangkat.

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

Merender aliran video peserta jarak jauh

Peserta jarak jauh dapat memulai berbagi video atau layar selama panggilan.

Handel berbagi video atau streaming berbagi layar peserta jarak jauh

Untuk mencantumkan aliran peserta jarak jauh, periksa videoStreams koleksi.

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

Dapatkan properti aliran video jarak jauh

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

Render aliran peserta jarak jauh

Untuk mulai merender aliran peserta jarak jauh, gunakan kode berikut.

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)

Dapatkan metode dan properti penyaji video jarak jauh

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

Siapkan sistem Anda

Membuat proyek Visual Studio

Untuk aplikasi UWP, di Visual Studio 2022, buat proyek Aplikasi Kosong (Universal Windows) baru. Setelah Anda memasukkan nama proyek, jangan ragu untuk memilih Windows SDK apa pun yang lebih baru dari 10.0.17763.0.

Untuk aplikasi WinUI 3, buat proyek baru dengan templat Aplikasi Kosong, Dipaketkan (WinUI 3 di Desktop) untuk menyiapkan aplikasi WinUI 3 satu halaman. SDK Aplikasi Windows versi 1.3 atau yang lebih baru diperlukan.

Instal paket dan dependensi dengan menggunakan NuGet Package Manager

API dan pustaka SDK Panggilan tersedia untuk umum melalui paket NuGet.

Langkah-langkah berikut ini mencontohkan cara menemukan, mengunduh, dan menginstal paket NuGet SDK Panggilan:

  1. Buka Manajer Paket NuGet dengan memilih Alat>Pengelola>Paket NuGet Kelola Paket NuGet untuk Solusi.
  2. Pilih Telusuri, lalu masukkan Azure.Communication.Calling.WindowsClient di kotak pencarian.
  3. Pastikan bahwa kotak centang Sertakan prarilis dipilih.
  4. Azure.Communication.Calling.WindowsClient Pilih paket, lalu pilih Azure.Communication.Calling.WindowsClient1.4.0-beta.1 atau versi yang lebih baru.
  5. Pilih kotak centang yang sesuai dengan proyek Communication Services di tab sisi kanan.
  6. Pilih tombol Instal.

Minta akses ke mikrofon

Aplikasi ini memerlukan akses ke kamera untuk berjalan dengan benar. Di aplikasi UWP, kemampuan kamera harus dideklarasikan dalam file manifes aplikasi.

Langkah-langkah berikut mencontohkan cara mencapainya.

  1. Di panel Solution Explorer, klik dua kali pada file dengan ekstensi .appxmanifest.
  2. Klik tab Capabilities.
  3. Pilih kotak centang Camera dari daftar kapabilitas.

Buat tombol UI untuk membuat dan menutup panggilan

Aplikasi sampel sederhana ini berisi dua tombol. Satu untuk membuat panggilan dan satu lagi untuk menutup panggilan. Langkah-langkah berikut menunjukkan contoh cara menambahkan tombol ini ke aplikasi.

  1. Di panel Solution Explorer , klik dua kali pada file bernama MainPage.xaml untuk UWP, atau MainWindows.xaml untuk WinUI 3.
  2. Di panel tengah, cari kode XMAL di bawah pratinjau UI.
  3. Ubah kode XAML dengan kutipan berikut:
<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>

Menyiapkan aplikasi dengan Calling SDK API

Calling SDK API berada di dua namespace yang berbeda. Langkah-langkah berikut memberikan informasi untuk pengompilasi C# tentang namespace layanan ini yang memungkinkan Intellisense Visual Studio membantu pengembangan kode.

  1. Di panel Solution Explorer , klik panah di sisi kiri file bernama MainPage.xaml untuk UWP, atau MainWindows.xaml untuk WinUI 3.
  2. Klik dua kali pada file bernama MainPage.xaml.cs atau MainWindows.xaml.cs.
  3. Tambahkan perintah berikut di bagian bawah pernyataan using saat ini.
using Azure.Communication.Calling.WindowsClient;

Simpan MainPage.xaml.cs atau MainWindows.xaml.cs buka. Langkah-langkah berikutnya akan menambahkan lebih banyak kode ke dalamnya.

Mengizinkan interaksi aplikasi

Tombol UI yang sebelumnya ditambahkan perlu dioperasikan di atas CommunicationCall yang ditempatkan. Ini berarti bahwa CommunicationCall anggota data harus ditambahkan ke MainPage kelas atau MainWindow . Selain itu, untuk memungkinkan operasi asinkron yang membuat CallAgent berhasil, data anggota CallAgent juga harus ditambahkan ke kelas yang sama.

Tambahkan anggota data berikut ke MainPage kelas atau MainWindow :

CallAgent callAgent;
CommunicationCall call;

Membuat pengatur tombol

Sebelumnya, dua tombol UI ditambahkan ke kode XAML. Kode berikut menambahkan pengatur untuk dieksekusi saat pengguna memilih tombol. Kode berikut harus ditambahkan setelah anggota data dari bagian sebelumnya.

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

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

Model objek

Kelas dan antarmuka berikut menangani beberapa fitur utama dari pustaka klien Azure Communication Services Calling untuk UWP.

Nama Deskripsi
CallClient CallClient adalah titik masuk utama ke pustaka klien Panggilan.
CallAgent CallAgent digunakan untuk memulai dan menggabungkan panggilan.
CommunicationCall CommunicationCall digunakan untuk mengelola panggilan yang ditempatkan atau digabungkan.
CommunicationTokenCredential CommunicationTokenCredential digunakan sebagai kredensial token untuk membuat instans CallAgent.
CallAgentOptions berisi CallAgentOptions informasi untuk mengidentifikasi pemanggil.
HangupOptions Menginformasikan HangupOptions apakah panggilan harus dihentikan kepada semua pesertanya.

Mendaftarkan handler skema video

Komponen UI, seperti MediaElement XAML atau MediaPlayerElement, Anda memerlukan aplikasi yang mendaftarkan konfigurasi untuk merender umpan video lokal dan jarak jauh. Tambahkan konten berikut di Package antara tag Package.appxmanifest:

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

Menginisialisasi CallAgent

Untuk membuat CallAgent instans dari CallClient, Anda harus menggunakan CallClient.CreateCallAgentAsync metode yang secara asinkron mengembalikan CallAgent objek setelah diinisialisasi.

Untuk membuat CallAgent, Anda harus melewati objek CallTokenCredential dan objek CallAgentOptions. Perlu diingat bahwa CallTokenCredential melempar jika token cacat dilewati.

Kode berikut harus ditambahkan di dalam dan fungsi pembantu untuk dipanggil dalam inisialisasi aplikasi.

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> Ubah dengan token kredensial yang valid untuk sumber daya Anda. Lihat dokumentasi token akses pengguna jika token info masuk harus memiliki sumber.

Lakukan panggilan 1:1 dengan kamera video

Objek yang diperlukan untuk membuat CallAgent sekarang sudah siap. Saatnya membuat dan melakukan panggilan video secara CallAgent asinkron.

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

Pratinjau kamera lokal

Kita dapat secara opsional menyiapkan pratinjau kamera lokal. Video dapat dirender melalui MediaPlayerElement:

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

Untuk menginisialisasi pratinjau MediaPlayerElementlokal :

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

Merender aliran kamera jarak jauh

Siapkan bahkan handler sebagai respons terhadap OnCallsUpdated peristiwa:

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

Mulai merender aliran video jarak jauh pada 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;
    }
}

Mengakhiri panggilan

Setelah panggilan dilakukan, metode HangupAsync objek CommunicationCall harus digunakan untuk menutup panggilan.

Instans HangupOptions juga harus digunakan untuk menginformasikan apakah panggilan harus diakhiri kepada semua pesertanya.

Kode berikut harus ditambahkan di dalam 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;
            }
        }
    }
}

Menjalankan kode

Pastikan Visual Studio membuat aplikasi untuk x64, x86 atau ARM64, lalu tekan F5 untuk mulai menjalankan aplikasi. Setelah itu, klik tombol CommunicationCall untuk melakukan panggilan ke penerima panggilan yang ditentukan.

Perlu diingat bahwa pertama kali aplikasi berjalan, sistem meminta pengguna untuk memberikan akses ke mikrofon.

Langkah berikutnya