Azure Communication Services SDK を使用してビデオ通話を管理する方法について説明します。 通話内のビデオの受信と送信を管理する方法について説明します。
前提条件
- アクティブなサブスクリプションが含まれる Azure アカウント。 無料でアカウントを作成できます。
- デプロイ済みの Communication Services リソース。 Communication Services リソースを作成します。
- 通話クライアントを有効にするためのユーザー アクセス トークン。 詳細については、アクセス トークンの作成と管理に関する記事を参照してください。
- 省略可能: クイックスタートを完了して、アプリケーションに音声通話を追加します
SDK のインストール
npm install
コマンドを使用して、JavaScript 用の Azure Communication Services の Common SDK と Calling SDK をインストールします。
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
必要なオブジェクトを初期化する
CallClient
インスタンスは、ほとんどの通話操作に必要です。 新しい CallClient
インスタンスを作成する際に、Logger
インスタンスなどのカスタム オプションを使用してこれを構成できます。
CallClient
インスタンスでは、CallAgent
を呼び出すことで createCallAgent
インスタンスを作成できます。 このメソッドでは、非同期的に CallAgent
インスタンス オブジェクトが返されます。
createCallAgent
メソッドでは、CommunicationTokenCredential
が引数として使用されます。 これは、ユーザー アクセス トークンを受け取ります。
getDeviceManager
インスタンスで CallClient
メソッドを使用して、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()
Microsoft インフラストラクチャへの SDK 接続を管理する
Call Agent
インスタンスは、(呼び出しを結合または開始するために) 呼び出しを管理するのに役立ちます。 呼び出しの SDK を機能させるには、Microsoft インフラストラクチャに接続して着信呼び出しの通知を取得し、他の呼び出しの詳細を調整する必要があります。
Call Agent
には、次の 2 つの状態があります。
接続済み - Call Agent
の Connected
connectionStatue 値は、クライアント SDK が接続されており、Microsoft インフラストラクチャから通知を受信できることを意味します。
切断済み - Call Agent
の Disconnected
connectionStatue 値は、SDK の正常な接続を妨げる問題があることを示します。
Call Agent
を再作成する必要があります。
-
invalidToken
: トークンが有効期限切れであるか、無効な場合、Call Agent
インスタンスがこのエラーで切断されます。 -
connectionIssue
: クライアントが Microsoft インフラストラクチャに接続する際に問題が発生した場合、多数の再試行ののちにCall Agent
にconnectionIssue
エラーが表示されます。
Call Agent
プロパティの現在の値を調べて、ローカル connectionState
が Microsoft インフラストラクチャに接続されているかどうかを確認できます。 アクティブな呼び出し中に、connectionStateChanged
イベントをリッスンして、Call Agent
の状態が接続済みから切断済みに変化するかどうかを判断できます。
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);
デバイス管理
Calling SDK でのビデオの使用を開始するには、デバイスを管理できることが必要になります。 デバイスを使用すると、音声とビデオを通話に送信する内容を制御できます。
deviceManager
を使用して、通話でオーディオ ストリームとビデオ ストリームを送信できるローカル デバイスを列挙します。 また、deviceManager
を使用して、ローカル デバイスのマイクやカメラにアクセスするためのアクセス許可を要求することもできます。
deviceManager
メソッドを呼び出すことによって callClient.getDeviceManager()
にアクセスできます。
const deviceManager = await callClient.getDeviceManager();
ローカル デバイスを取得する
ローカル デバイスにアクセスするには、deviceManager
列挙メソッド getCameras()
と getMicrophones
を使用します。 これらのメソッドは非同期アクションです。
// 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...]
既定のデバイスを設定する
使用可能なデバイスがわかったら、マイク、スピーカー、カメラの既定のデバイスを設定できます。 クライアントの既定値が設定されていない場合、Communication Services SDK ではオペレーティング システムの既定値が使用されます。
マイク
使用されているデバイスにアクセス
// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;
使用するデバイスを設定
// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);
スピーカー
使用されているデバイスにアクセス
// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
使用するデバイスを設定
// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);
カメラ
使用されているデバイスにアクセス
// Get the camera device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
使用するデバイスを設定
// Set the speaker device to use.
await deviceManager.selectSpeaker(localCameras[0]);
それぞれの CallAgent
が自分のマイクとスピーカーを、関連付けられた DeviceManager
で選択することができます。 異なる CallAgents
は異なるマイクとスピーカーを使用することをお勧めします。 同じマイクやスピーカーを共有するべきではありません。 共有が行われると、マイク ユーザー向け診断 (UFD) がトリガーされ、ブラウザーと OS によってはマイクの動作が停止する可能性があります。
ローカル ビデオ ストリーム
ユーザーが通話でビデオを送信するには、 LocalVideoStream
オブジェクトを作成する必要があります。
const localVideoStream = new LocalVideoStream(camera);
パラメーターとして渡されるカメラは、VideoDeviceInfo
メソッドによって返されるdeviceManager.getCameras()
オブジェクトです。
LocalVideoStream
には次のプロパティがあります。
source
はデバイス情報です。const source = localVideoStream.source;
mediaStreamType
は、Video
、ScreenSharing
、またはRawMedia
のいずれかです。const type: MediaStreamType = localVideoStream.mediaStreamType;
ローカル カメラのプレビュー
deviceManager
と VideoStreamRenderer
を使用して、ローカル カメラからのストリームのレンダリングを開始できます。
LocalVideoStream
を作成した後、それを使用してVideoStreamRenderer
を設定します。
VideoStreamRenderer
を作成したら、そのcreateView()
メソッドを呼び出して、子としてページに追加できるビューを取得します。
このストリームは他の参加者には送信されません。 これはローカル プレビュー フィードです。
// 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);
ローカル プレビューを停止する
ローカル プレビュー通話を停止するには、VideoStreamRenderer
から派生したビューを破棄します。
VideoStreamRenderer が破棄されたら、プレビューを含む DOM Node から removeChild()
メソッドを呼び出して、html ツリーから ビューを削除します。
// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);
カメラとマイクへのアクセス許可を要求する
アプリケーションは、アクセス許可なしでカメラやマイクを使用できません。 deviceManager を使用して、カメラやマイクへのアクセス許可を付与するようにユーザーに求めることができます。
const result = await deviceManager.askDevicePermission({audio: true, video: true});
Promise が解決されたら、メソッドは、DeviceAccess
と audio
のアクセス許可が付与されたかどうかを示す video
オブジェクトと共に返ります。
console.log(result.audio);
console.log(result.video);
メモ
- ビデオ デバイスが接続されたり、取り外されたりすると、
videoDevicesUpdated
イベントが発生します。 - オーディオ デバイスが接続されると、
audioDevicesUpdated
イベントが発生します。 -
DeviceManager
を初めて作成したとき、アクセス許可がまだ付与されていない場合、デバイスは認識されません。 最初は、そのデバイス名は空で、詳細なデバイス情報は含まれません。 ユーザーにデバイス アクセスを求めるDeviceManager.askPermission()
を呼び出す必要があります。 ユーザーがアクセスを許可すると、デバイス マネージャーはシステム上のデバイスについて学習し、デバイスリストを更新し、audioDevicesUpdated
イベントとvideoDevicesUpdated
イベントを送信します。 ユーザーがページを更新してデバイス マネージャーを作成した場合、デバイス マネージャーは、ユーザーが以前にアクセス権を付与したため、デバイスについて学習します。 デバイス リストが最初に入力され、audioDevicesUpdated
イベントやvideoDevicesUpdated
イベントは生成されません。 - スピーカーの列挙や選択は、Android の Chrome、iOS の Safari、macOS の Safari のいずれでもサポートされていません。
ビデオ カメラを使用して呼び出しを行う
重要
現在、サポートされている発信ローカル動画ストリームは 1 つだけです。
ビデオ通話を行うには、getCameras()
の deviceManager
メソッドを使ってローカル カメラを列挙する必要があります。
カメラを選択したら、それを使用して LocalVideoStream
インスタンスを作成します。
videoOptions
内のそれを localVideoStream
配列内の項目として 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);
-
CallAgent.join()
API を使用してビデオを使用して通話に参加し、Call.Accept()
API を使用してビデオを受け入れて呼び出す方法も可能です。 - 通話が接続されると、選択したカメラから他の参加者への動画ストリームの送信が自動的に開始されます。
通話中にローカル ビデオの送信を開始および停止する
ビデオを開始する
呼び出し中に動画を開始するには、getCameras
オブジェクトで deviceManager
メソッドを使用して、カメラを列挙する必要があります。
次に、目的のカメラを使用して LocalVideoStream
の新しいインスタンスを作成し、その LocalVideoStream
オブジェクトを既存の呼び出しオブジェクトの startVideo
メソッドに渡します。
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);
ビデオを停止
動画の送信が正常に開始されると、通話インスタンスの LocalVideoStream
コレクションに種類が Video
の localVideoStreams
インスタンスが追加されます。
Call オブジェクトでビデオ ストリームを見つける
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );
ローカル ビデオを停止する 通話中にローカルビデオを停止するには、ビデオで使用されている localVideoStream
インスタンスを Call
の stopVideo メソッドに渡します。
await call.stopVideo(localVideoStream);
その switchSource
インスタンスで LocalVideoStream
を呼び出すことで、アクティブな LocalVideoStream を保持しつつ、別のカメラ デバイスに切り替えることができます。
const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);
指定したビデオ デバイスが使用できない場合は、次のようにします。
- 通話中にビデオがオフの状態で
call.startVideo()
を使用してビデオを開始すると、このメソッドはSourceUnavailableError
をスローし、cameraStartFailed
のユーザー向け診断が true に設定されます。 -
localVideoStream.switchSource()
メソッドの呼び出しによりcameraStartFailed
が true に設定されます。 通話診断ガイドによって、通話関連の問題を診断する方法に関する追加情報が提供されます。
ローカル ビデオのオン/オフを確認するには、Call
メソッド isLocalVideoStarted
を使用します。これは true または false を返します。
// Check if local video is on or off
call.isLocalVideoStarted;
ローカルビデオの変更をリッスンするには、isLocalVideoStartedChanged イベントに登録したり解除したりできます。
// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
// Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
// Callback();
});
通話中に画面共有を開始および停止する
通話中に画面共有を開始するには、startScreenSharing()
オブジェクトに対して非同期メソッド Call
を使用します。
画面共有を開始する
// Start screen sharing
await call.startScreenSharing();
注
画面共有の送信は、デスクトップ ブラウザーでのみサポートされています。
LocalVideoStream のコレクションで画面共有を見つける
画面共有の送信が正常に開始されると、LocalVideoStream
型のScreenSharing
インスタンスが呼び出しインスタンスのlocalVideoStreams
コレクションに追加されます。
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );
画面共有を停止する
通話中に画面共有を停止するには、非同期 API stoptScreenSharing を使用します。
// Stop screen sharing
await call.stopScreenSharing();
画面共有の状態を確認する
画面共有がオンかオフかを確認する際は、isScreenSharingOn API を使用すると、true または false が返されます。
// Check if screen sharing is on or off
call.isScreenSharingOn;
画面共有の変更を監視するには、isScreenSharingOnChanged イベントをサブスクライブし、解除することができます。
// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
// Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
// Callback();
});
重要
Azure Communication Services のこの機能は、現在プレビュー段階にあります。 プレビュー段階の機能は一般公開されており、新規および既存の Microsoft のすべてのお客様が使用できます。
プレビューの API と SDK は、サービス レベル アグリーメントなしに提供されます。 運用環境のワークロードには使用しないことをお勧めします。 特定の機能がサポートされていないか、機能が制約されている可能性があります。
詳しくは、Microsoft Azure プレビューの追加使用条件に関するページをご覧ください。
ローカル画面共有プレビューはパブリック プレビューの段階であり、バージョン 1.15.1-beta.1+ の一部としてご利用になれます。
ローカル画面共有プレビュー
VideoStreamRenderer
を使用してローカル画面共有からのストリーム レンダリングを開始できるので、画面共有ストリームとして何を送信しているのかを見ることができます。
// 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 stopped 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);
}
});
リモート参加者の動画/画面共有ストリームをレンダリングする
リモート参加者のビデオまたは画面共有をレンダリングするには、最初の手順として、レンダリングする RemoteVideoStream の参照を取得します。
リモート参加者をレンダリングできるのは、videoStreams
の配列またはビデオ ストリーム (RemoteParticipant
) を経由することだけです。 リモート参加者コレクションには、Call
オブジェクトを介してアクセスします。
const remoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType = remoteVideoStream.mediaStreamType;
RemoteVideoStream
をレンダリングするには、そのisAvailableChanged
イベントをサブスクライブする必要があります。
isAvailable
プロパティが true
に変更される場合、リモート参加者はビデオ ストリームを送信しています。
その後、VideoStreamRenderer
の新しいインスタンスを作成し、非同期VideoStreamRendererView
メソッドを使用して新しいcreateView
インスタンスを作成します。 その後、任意の UI 要素に view.target
を付加できます。
リモート ストリームの使用可否が変わるたびに、VideoStreamRenderer
全体を破棄することも、特定の VideoStreamRendererView
を破棄することもできます。 それらを保持する場合、ビューには空のビデオ フレームが表示されます。
// 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。
.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); }
}
リモート動画の画質
Azure Communication Services WebJS SDK には、バージョン 1.15.1 以降の最適なビデオ数 (OVC) と呼ばれる機能が用意されています。
この機能を使用して、2 人以上の参加者のグループ通話で、特定の時点で最適にレンダリングできるさまざまな参加者からの受信ビデオの数を実行時にアプリケーションに通知します。
この機能が公開するプロパティ optimalVideoCount
は、ローカル エンドポイントのネットワークとハードウェアの機能に基づいて、呼び出し中に動的に変化します。
optimalVideoCount
の値は、さまざまな参加者アプリケーションから送られるどれだけのビデオを特定の時点でレンダリングするかについて詳細に示します。 アプリケーションはこれらの変更に対処して、レンダリングされたビデオの数を、推奨事項に従って更新する必要があります。 各更新の間にはデバウンス期間 (約 10 秒) があります。
使用方法
optimalVideoCount
機能は通話機能です。
OptimalVideoCount
オブジェクトの feature
メソッドを使用して、フィーチャー Call
を参照する必要があります。
その後、on
の OptimalVideoCountCallFeature
メソッドを使用してリスナーを設定し、optimalVideoCount が変更されたときに通知を受け取ることができます。 変更のサブスクライブを解除するには、off
メソッドを呼び出すことができます。
レンダリングできる現在の最大着信ビデオ数は 16 です。 16 個の受信ビデオを適切にサポートするには、コンピューターには 16 GB 以上の RAM と、3 歳未満の 4 コア以上の CPU が必要です。
const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})
使用例: アプリケーションは、グループ呼び出しで最適なビデオ数の変更をサブスクライブします。 最適なビデオ数の変更は、新しいレンダラー createView
メソッドを作成するか、ビュー dispose
破棄して、それに応じてアプリケーション レイアウトを更新することによって処理されます。
リモート動画ストリームのプロパティ
リモート動画ストリームには次のプロパティがあります。
const id: number = remoteVideoStream.id;
-
id
: リモート動画ストリームの ID。
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
-
mediaStreamType
:Video
またはScreenSharing
を指定できます。
const isAvailable: boolean = remoteVideoStream.isAvailable;
-
isAvailable
: リモート参加者のエンドポイントによりストリームがアクティブに送信されているかどうかを定義します。
const isReceiving: boolean = remoteVideoStream.isReceiving;
isReceiving
:リモート ビデオ ストリーム データを受信しているかどうかをアプリケーションに通知します。
次のシナリオでは、フラグが
false
に移行します。- モバイル ブラウザーのリモート参加者が、ブラウザー アプリをバックグラウンドに移動する。
- リモート参加者またはビデオを受信しているユーザーに、ビデオ品質に大きな影響を与えるネットワークの問題がある。
- macOS/iOS Safari のリモート参加者が、アドレス バーから [一時停止] を選択する。
- リモート参加者がネットワーク切断に遭っている。
- モバイルのリモート参加者が、ブラウザーを強制的または通常どおり終了する。
- モバイルまたはデスクトップのリモート参加者が、そのデバイスをロックする。 このシナリオは、リモート参加者がデスクトップ コンピューターで、スリープ状態になる場合にも適用されます。
次のシナリオでは、フラグが
true
に移行します。- モバイル ブラウザーのリモート参加者が、バックグラウンドにしていたブラウザーをフォアグラウンドに戻す。
- macOS/iOS Safari のリモート参加者は、ビデオを一時停止した後、アドレス バーから [再開 ] を選択します。
- リモート参加者が、一時的な切断の後、ネットワークに再接続する。
- モバイルのリモート参加者がデバイスのロックを解除し、モバイル ブラウザーで通話に戻る。
この機能により、リモート ビデオ ストリームのレンダリングに関するユーザー エクスペリエンスが向上します。
isReceiving フラグが false に変わると、読み込みスピナーをリモート ビデオ ストリーム上に表示できます。 読み込みスピナーの実装は必須ではありませんが、読み込みスピナーは、ユーザー エクスペリエンスの向上のために最も一般的に使用されているものです。
const size: StreamSize = remoteVideoStream.size;
size
: ビデオの幅と高さに関する情報を含むストリーム サイズ。
VideoStreamRenderer のメソッドとプロパティ
await videoStreamRenderer.createView();
リモート ビデオ ストリームをレンダリングするためにアプリケーション UI に付加できる VideoStreamRendererView
インスタンスを作成し、非同期 createView()
メソッドを使用します。これにより、ストリームをレンダリングする準備ができたときに解決され、DOM ツリー内のどこにでも挿入できる target
要素を表す video
プロパティを含むオブジェクトが返されます。
videoStreamRenderer.dispose();
videoStreamRenderer
と、それに関連付けられているすべての VideoStreamRendererView
インスタンスを破棄します。
VideoStreamRendererView のメソッドとプロパティ
VideoStreamRendererView
を作成するときに、scalingMode
と isMirrored
プロパティを指定できます。
scalingMode
は、Stretch
、Crop
、または Fit
のいずれかです。
isMirrored
を指定すると、レンダリングされたストリームは垂直方向に反転されます。
const videoStreamRendererView: VideoStreamRendererView = await videoStreamRenderer.createView({ scalingMode, isMirrored });
すべての VideoStreamRendererView
インスタンスには、レンダリング サーフェイスを表す target
プロパティがあります。 アプリケーション UI にこのプロパティを付加します。
htmlElement.appendChild(view.target);
scalingMode
は、updateScalingMode
メソッドを呼び出すことで更新できます。
view.updateScalingMode('Crop');
2 つの異なるカメラからの動画ストリームを、同じデスクトップ デバイスから同じ通話の中で送信します。
重要
Azure Communication Services のこの機能は、現在プレビュー段階にあります。 プレビュー段階の機能は一般公開されており、新規および既存の Microsoft のすべてのお客様が使用できます。
プレビューの API と SDK は、サービス レベル アグリーメントなしに提供されます。 運用環境のワークロードには使用しないことをお勧めします。 特定の機能がサポートされていないか、機能が制約されている可能性があります。
詳しくは、Microsoft Azure プレビューの追加使用条件に関するページをご覧ください。
同一の通話で 2 つの異なるカメラからビデオ ストリームを送信することは、デスクトップでサポートされているブラウザーでバージョン 1.17.1-beta.1 以降の一部としてサポートされています。
ひとつのデスクトップ ブラウザー タブ/アプリの 2 つの異なるカメラから動画ストリームを、同じ通話の中で、次のコード スニペットを使用して送信できます。
// 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();
制限事項:
- ビデオ ストリームの送信は、異なる ID を使用する 2 つの異なる
CallAgent
インスタンスで行う必要があります。 このコード スニペットは、2 つの通話エージェントが使用されており、それぞれが独自の Call オブジェクトをもっている状態を示しています。 - コード例では、両方の CallAgent が同じ呼び出し (同じ呼び出し ID) に参加しています。 また、エージェントごとに異なる通話に参加し、一方の通話では 1 つのビデオ、もう一方の通話では別のビデオを送信することもできます。
- 両方の CallAgent で同じカメラを送信することはサポートされていません。 これらは 2 つの異なるカメラでなければなりません。
- 2 つの異なるカメラをひとつの CallAgent で送信することは、現在はサポートされていません。
- macOS Safari では、背景ぼかし動画エフェクト (@azure/communication-effects) から) を適用できるのはひとつのカメラだけであり、両方に同時に適用することはできません。
SDK のインストール
プロジェクト レベルの build.gradle
ファイルを見つけて、mavenCentral()
と buildscript
の下のリポジトリの一覧に allprojects
を追加します。
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
次に、モジュール レベルの build.gradle
ファイルで、次の行を dependencies
セクションに追加します。
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
必要なオブジェクトを初期化する
CallAgent
インスタンスを作成するには、createCallAgent
インスタンス上で CallClient
メソッドを呼び出す必要があります。 この呼び出しは、CallAgent
インスタンス オブジェクトを非同期に返します。
createCallAgent
メソッドは、CommunicationUserCredential
をカプセル化する を引数として受け取ります。
DeviceManager
にアクセスするには、まず callAgent
インスタンスを作成する必要があります。 それから、CallClient.getDeviceManager
メソッドを使用して 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();
呼び出し元の表示名を設定するには、この代替メソッドを使用します。
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();
デバイス管理
通話でビデオを使用するには、デバイスを管理する必要があります。 デバイスを使用すると、音声とビデオを通話に送信する内容を制御できます。
DeviceManager
オブジェクトを使用すると、オーディオ/ビデオ ストリームを送信する呼び出しで使用するローカル デバイスを列挙できます。 また、ネイティブ ブラウザー API を使用して、マイクとカメラにアクセスするアクセス許可をユーザーに要求することもできます。
deviceManager
にアクセスするには、callClient.getDeviceManager()
メソッドを呼び出します。
Context appContext = this.getApplicationContext();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
ローカル デバイスを列挙する
ローカル デバイスにアクセスするには、デバイス マネージャーで列挙メソッドを使用します。 列挙は同期アクションです。
// Get a list of available video devices for use.
List<VideoDeviceInfo> localCameras = deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
ローカル カメラのプレビュー
DeviceManager
と Renderer
を使用して、ローカル カメラからのストリームのレンダリングを開始できます。 このストリームは他の参加者には送信されません。 これはローカル プレビュー フィードです。 ストリームのレンダリングは非同期アクションです。
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 の通話を行う
警告
現在、サポートされている発信ローカル動画ストリームは 1 つだけです。 ビデオを使用して呼び出しを行うには、 deviceManager
getCameras
API を使用してローカル カメラを列挙する必要があります。
カメラを選択したら、それを使用してLocalVideoStream
インスタンスを構築し、videoOptions
配列内の項目としてlocalVideoStream
にcall
メソッドに渡します。 通話が接続されると、選択したカメラから他の参加者へのビデオ ストリームの送信が自動的に開始されます。
注
プライバシーに関する懸念があるため、ビデオがローカルでプレビューされていない場合、ビデオは通話に共有されません。 詳細については、「 ローカル カメラのプレビュー」を参照してください。
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);
ローカル動画の送信を開始および停止する
ビデオを開始するには、オブジェクトに対する getCameraList
操作を使用してカメラ deviceManager
列挙する必要があります。 次に、目的のカメラを渡して LocalVideoStream
の新しいインスタンスを作成し、それを引数として startVideo
API に渡します。
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();
ビデオの送信が正常に開始されると、 LocalVideoStream
インスタンスが呼び出しインスタンスの localVideoStreams
コレクションに追加されます。
List<LocalVideoStream> videoStreams = call.getLocalVideoStreams();
LocalVideoStream currentLocalVideoStream = videoStreams.get(0); // Please make sure there are VideoStreams in the list before calling get(0).
ローカル動画を停止するには、LocalVideoStream
コレクションで使用可能な localVideoStreams
インスタンスを渡します。
call.stopVideo(appContext, currentLocalVideoStream).get();
switchSource
インスタンスで LocalVideoStream
を呼び出すことにより、動画の送信中に別のカメラ デバイスに切り替えることができます。
currentLocalVideoStream.switchSource(source).get();
リモート参加者の動画ストリームをレンダリングする
リモート参加者の動画ストリームと画面共有ストリームの一覧を取得するには、videoStreams
コレクションを調べます。
List<RemoteParticipant> remoteParticipants = call.getRemoteParticipants();
RemoteParticipant remoteParticipant = remoteParticipants.get(0); // Please make sure there are remote participants in the list before calling get(0).
List<RemoteVideoStream> remoteStreams = remoteParticipant.getVideoStreams();
RemoteVideoStream remoteParticipantStream = remoteStreams.get(0); // Please make sure there are video streams in the list before calling get(0).
MediaStreamType streamType = remoteParticipantStream.getType(); // of type MediaStreamType.Video or MediaStreamType.ScreenSharing
リモート参加者からの RemoteVideoStream
をレンダリングするには、OnVideoStreamsUpdated
イベントをサブスクライブする必要があります。
イベント内で isAvailable
プロパティが true に変更された場合、リモート参加者が現在ストリームを送信していることを示します。 それが発生したら、Renderer
の新しいインスタンスを作成し、非同期 RendererView
API を使用して新しい createView
を作成し、お使いのアプリケーションの UI の任意の場所に view.target
をアタッチします。
リモート ストリームの可用性が変化するたびに、 Renderer
全体を破棄するか、特定の RendererView
を保持するかを選択できますが、空のビデオ フレームが表示されます。
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();
}
}
}
リモート動画ストリームのプロパティ
リモート ビデオ ストリームには、次のプロパティがあります。
Id
- リモート ビデオ ストリームの ID。int id = remoteVideoStream.getId();
MediaStreamType
-Video
またはScreenSharing
できます。MediaStreamType type = remoteVideoStream.getMediaStreamType();
isAvailable
- リモート参加者エンドポイントがストリームをアクティブに送信しているかどうかを示します。boolean availability = remoteVideoStream.isAvailable();
Renderer のメソッドとプロパティ
Renderer
オブジェクトは、次のメソッドを使用します。
リモート ビデオ ストリームをレンダリングするには、後でアプリケーション UI にアタッチできる
VideoStreamRendererView
インスタンスを作成します。// Create a view for a video stream VideoStreamRendererView.createView()
レンダラーと、このレンダラーに関連付けられているすべての
VideoStreamRendererView
を破棄します。 関連付けられているすべてのビューを UI から削除した後に呼び出します。VideoStreamRenderer.dispose()
リモート ビデオ ストリームのサイズ (幅/高さ) を設定するには、
StreamSize
を使用します。StreamSize renderStreamSize = VideoStreamRenderer.getSize(); int width = renderStreamSize.getWidth(); int height = renderStreamSize.getHeight();
RendererView のメソッドとプロパティ
VideoStreamRendererView
を作成するときに、このビューに適用するScalingMode
プロパティとmirrored
プロパティを指定できます。
スケーリング モードには、 CROP
または FIT
のいずれかを指定できます。
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteVideoStream, appContext);
VideoStreamRendererView rendererView = remoteVideoRenderer.createView(new CreateViewOptions(ScalingMode.Fit));
作成した RendererView は、次のスニペットを使用してアプリケーションの UI にアタッチできます。
layout.addView(rendererView);
後で、updateScalingMode
またはRendererView
の引数を使用して、ScalingMode.CROP
オブジェクトに対するScalingMode.FIT
操作を使用してスケーリング モードを更新できます。
// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)
システムを設定する
次の手順のようにして、システムを設定します。
Xcode プロジェクトを作成する
Xcode で、新しい iOS プロジェクトを作成し、[単一ビュー アプリ] テンプレートを選択します。 この記事では SwiftUI フレームワークを使うので、[言語] を [Swift] に、[インターフェイス] を [SwiftUI] に設定する必要があります。
この記事では、テストは作成しません。 [Include Tests] チェック ボックスはオフにしてもかまいません。
CocoaPods を使用してパッケージと依存関係をインストールする
この例のように、アプリケーション用の Podfile を作成します。
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
pod install
を実行します。Xcode を使用して
.xcworkspace
を開きます。
マイクへのアクセスを要求する
デバイスのマイクにアクセスするには、NSMicrophoneUsageDescription
を使用してアプリの情報プロパティ一覧を更新する必要があります。 関連付けられる値には、システムがユーザーにアクセスを要求するために使うダイアログに含まれる文字列を設定します。
プロジェクト ツリーの [Info.plist] エントリを右クリックし、[Open As]> を選択します。 最上位の <dict>
セクションに以下の行を追加してから、ファイルを保存します。
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
アプリのフレームワークを設定する
プロジェクトの ContentView.swift
ファイルを開きます。 ファイルの先頭に import
宣言を追加して、AzureCommunicationCalling
ライブラリをインポートします。 さらに、AVFoundation
をインポートします。 これは、コードでのオーディオ アクセス許可の要求に必要です。
import AzureCommunicationCalling
import AVFoundation
CallAgent を初期化する
CallAgent
から CallClient
インスタンスを作成するには、初期化された後に callClient.createCallAgent
オブジェクトを非同期に返す CallAgent
メソッドを使用する必要があります。
通話クライアントを作成するには、CommunicationTokenCredential
オブジェクトを渡します。
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
オブジェクトを CallClient
に渡し、表示名を設定します。
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")
}
})
デバイスの管理
通話でビデオの使用を開始するには、デバイスを管理する方法を知っている必要があります。 デバイスを使用すると、音声とビデオを通話に送信する内容を制御できます。
DeviceManager
を使用すると、オーディオまたは動画のストリームを送信するために通話内で使用できるローカル デバイスを列挙できます。 また、マイクまたはカメラにアクセスするためのアクセス許可をユーザーに要求することもできます。
deviceManager
オブジェクトの callClient
にアクセスできます。
self.callClient!.getDeviceManager { (deviceManager, error) in
if (error == nil) {
print("Got device manager instance")
self.deviceManager = deviceManager
} else {
print("Failed to get device manager instance")
}
}
ローカル デバイスを列挙する
ローカル デバイスにアクセスする場合は、デバイス マネージャーで列挙メソッドを使用できます。 列挙は同期アクションです。
// enumerate local cameras
var localCameras = deviceManager.cameras // [VideoDeviceInfo, VideoDeviceInfo...]
ローカル カメラのプレビューを取得する
Renderer
を使用して、ローカル カメラからのストリームのレンダリングを開始できます。 このストリームは他の参加者には適用されません。これはローカル プレビュー フィードです。 これは、非同期アクションです。
let camera: VideoDeviceInfo = self.deviceManager!.cameras.first!
let localVideoStream = LocalVideoStream(camera: camera)
let localRenderer = try! VideoStreamRenderer(localVideoStream: localVideoStream)
self.view = try! localRenderer.createView()
ローカル カメラのプレビューのプロパティを取得する
レンダラーには、レンダリングを制御できる一連のプロパティとメソッドが含まれています。
// 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 の通話を行う
デバイス マネージャーのインスタンスを取得する場合は、デバイスの管理に関するセクションを参照してください。
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")
}
}
リモート参加者の動画ストリームをレンダリングする
リモート参加者は、通話中に動画または画面共有を開始できます。
リモート参加者の動画共有または画面共有ストリームを処理する
リモート参加者のストリームを一覧表示するには、videoStreams
コレクションを調べます。
var remoteParticipantVideoStream = call.remoteParticipants[0].videoStreams[0]
リモート動画ストリームのプロパティを取得する
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
リモート参加者ストリームをレンダリングする
リモート参加者ストリームのレンダリングを開始するには、次のコードを使用します。
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)
リモート動画レンダラーのメソッドとプロパティを取得する
// [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()
システムを設定する
次の手順のようにして、システムを設定します。
Visual Studio プロジェクトの作成
ユニバーサル Windows プラットフォーム アプリを作成する場合は、Visual Studio 2022 で新しい空のアプリケーション (ユニバーサル Windows)プロジェクトを作成します。 プロジェクト名を入力した後、10.0.17763.0 より後の Windows SDK を自由に選択できます。
WinUI 3 アプリの場合、Blank App, Packaged (WinUI 3 in Desktop) テンプレートで新しいプロジェクトを作成し、シングルページの WinUI 3 アプリを設定します。 Windows App SDK バージョン 1.3 以降が必要です。
NuGet パッケージ マネージャーを使用してパッケージと依存関係をインストールする
Calling SDK の API とライブラリは、NuGet パッケージにより一般公開されています。
Calling SDK NuGet パッケージを検索、ダウンロード、インストールするには:
- [ツール]>[NuGet パッケージ マネージャー]>[ソリューションの NuGet パッケージの管理] を選んで、NuGet パッケージ マネージャーを開きます。
- [参照] を選んでから、検索ボックスに「Azure.Communication.Calling.WindowsClient」と入力します。
- [プレリリースを含める] チェックボックスがオンになっていることを確認します。
- Azure.Communication.Calling.WindowsClient パッケージを選び、Azure.Communication.Calling.WindowsClient1.4.0-beta.1 以降のバージョンを選びます。
- 右側のペインで、Azure Communication Services プロジェクトに対応するチェックボックスをオンにします。
- [インストール] を選択します。
マイクへのアクセスを要求する
アプリにはカメラへのアクセスが必要です。 ユニバーサル Windows プラットフォーム (UWP) アプリでは、アプリ マニフェスト ファイルでカメラ機能を宣言する必要があります。
- Visual Studio でプロジェクトを開きます。
-
ソリューション エクスプローラー パネルで、拡張子が
.appxmanifest
ファイルをダブルクリックします。 - [機能] タブ を クリックします。
- 機能の一覧から [
Camera
] チェック ボックスをオンにします。
通話の発信と終了を行うための UI ボタンを作成する
このサンプル アプリには、2 つのボタンが含まれています。 1 つは通話を発信するためのもので、もう 1 つは発信した通話を終了するためのものです。
-
ソリューション エクスプローラー パネルで、UWP の場合は
MainPage.xaml
、WinUI 3 の場合はMainWindows.xaml
という名前のファイルをダブルクリックします。 - 中央のパネルで、UI プレビューの下にある XAML コードを探します。
- 次の抜粋を使用して XAML コードを変更します。
<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>
Calling SDK API を使用したアプリの設定
Calling SDK API は、2 つの異なる名前空間に含まれています。
次の手順を実行して、これらの名前空間について C# コンパイラに通知し、Visual Studio の Intellisense がコード開発を支援できるようにします。
-
ソリューション エクスプローラー パネルで、UWP の場合は
MainPage.xaml
、WinUI 3 の場合はMainWindows.xaml
という名前のファイルの左側にある矢印をクリックします。 -
MainPage.xaml.cs
またはMainWindows.xaml.cs
という名前のファイルをダブルクリックします。 - 現在の
using
ステートメントの下部に次のコマンドを追加します。
using Azure.Communication.Calling.WindowsClient;
MainPage.xaml.cs
または MainWindows.xaml.cs
は開いたままにします。 次の手順では、さらにコードを追加します。
アプリの操作を有効にする
追加した UI ボタンは、配置された CommunicationCall
の上で操作する必要があります。 つまり、 CommunicationCall
データ メンバーを MainPage
または MainWindow
クラスに追加する必要があります。
また、 CallAgent
を作成する非同期操作を有効にして成功させる必要もあります。
CallAgent
データ メンバーを同じクラスに追加します。
MainPage
または MainWindow
クラスに次のデータ メンバーを追加してください。
CallAgent callAgent;
CommunicationCall call;
ボタン ハンドラーを作成する
以前は、XAML コードに 2 つの UI ボタンを追加しました。 次のコードは、ユーザーがボタンを選択したときに実行するハンドラーを追加します。
前のセクションのデータ メンバーの後に次のコードを追加します。
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// End the current call
}
オブジェクト モデル
UWP 用の Azure Communication Services Calling クライアント ライブラリが備える主な機能のいくつかは、以下のクラスとインターフェイスにより処理されます。
名前 | 説明 |
---|---|
CallClient |
CallClient は、通話クライアント ライブラリへのメイン エントリ ポイントです。 |
CallAgent |
CallAgent は、通話を開始して参加するために使用します。 |
CommunicationCall |
CommunicationCall は、開始または参加した通話の管理に使用されます。 |
CommunicationTokenCredential |
CommunicationTokenCredential は、CallAgent をインスタンス化するためのトークン資格情報として使用されます。 |
CallAgentOptions |
CallAgentOptions には、呼び出し元を識別するための情報が含まれています。 |
HangupOptions |
HangupOptions は、呼び出しを終了する必要があるかどうかをすべての参加者に対して通知します。 |
ビデオ スキーマ ハンドラーの登録
XAML の MediaElement
や MediaPlayerElement
などの UI コンポーネントでは、ローカルおよびリモートのビデオ フィードをレンダリングするための構成を登録する必要があります。
Package
の Package.appxmanifest
タグの間に次の内容を追加してください。
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
CallAgent を初期化する
CallAgent
から CallClient
インスタンスを作成するには、初期化されると CallClient.CreateCallAgentAsync
オブジェクトを非同期に返す CallAgent
メソッドを使用する必要があります。
CallAgent
を作成するには、CallTokenCredential
オブジェクトと CallAgentOptions
オブジェクトを渡す必要があります。 形式に誤りがあるトークンが渡されると、CallTokenCredential
がスローされることに注意してください。
初期化中に実行されるように、次のコードを内部に追加し、ヘルパー関数を追加します。
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>
を変更します。 資格情報トークンのソーシングの詳細については、「 ユーザー アクセス トークン」を参照してください。
ビデオ カメラを使用して 1:1 の通話を行う
CallAgent
を作成するために必要なオブジェクトの準備ができました。 次に、 CallAgent
を非同期的に作成し、ビデオ通話を行います。
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 GetStartCallOptionsAsync();
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 LocalOutgoingVideoStream(this.viceManager.Cameras.FirstOrDefault() as VideoDeviceDetails); // Create a default video stream
private async Task<StartCallOptions> GetStartCallOptionsAsync()
{
return new StartCallOptions() {
OutgoingAudioOptions = new OutgoingAudioOptions() { IsMuted = true, Stream = micStream },
OutgoingVideoOptions = new OutgoingVideoOptions() { Streams = new OutgoingVideoStream[] { cameraStream } }
};
}
ローカル カメラのプレビュー
必要に応じて、ローカル カメラのプレビューを設定できます。 次の MediaPlayerElement
を使用してビデオをレンダリングできます。
<Grid>
<MediaPlayerElement x:Name="LocalVideo" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" AutoPlay="True" />
</Grid>
ローカル プレビュー 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 selectedCamera = CameraList.SelectedItem as VideoDeviceDetails;
cameraStream = new LocalOutgoingVideoStream(selectedCamera);
var localUri = await cameraStream.StartPreviewAsync();
LocalVideo.Source = MediaSource.CreateFromUri(localUri);
if (this.call != null) {
await this.call?.StartVideoAsync(cameraStream);
}
}
リモート カメラ ストリームをレンダリングする
OnCallsUpdated
イベントに応答して偶数ハンドラーを設定します。
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;
}
}
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;
}
}
通話を終了する
呼び出しが行われたら、HangupAsync
オブジェクトの CommunicationCall
メソッドを使用して呼び出しを切断します。
呼び出しを終了する必要があるかどうかを参加者に通知するには、 HangupOptions
のインスタンスを使用します。
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;
}
}
}
}
コードの実行
- Visual Studio で、
x64
、x86
、またはARM64
用のアプリがビルドされていることを確認します。 - F5 キーを押してアプリの実行を開始します。
- [ CommunicationCall ] ボタンをクリックして、定義された受信者に通話を発信します。
アプリを初めて実行すると、マイクへのアクセスを許可するようにユーザーに求められます。