Hızlı Başlangıç: Oda çağrısına katılma
Önkoşullar
- Etkin aboneliği olan bir Azure hesabı. Ücretsiz hesap oluşturun.
- Etkin bir İletişim Hizmetleri kaynağı ve bağlantı dizesi. İletişim Hizmetleri kaynağı oluşturun.
- İki veya daha fazla İletişim Kullanıcı Kimliği. Test için erişim belirteçleri veya Hızlı oluşturma kimlikleri oluşturun ve yönetin.
- Oluşturulan oda ve katılımcı buna eklendi. Oda oluşturma ve yönetme
Kullanıcı erişim belirtecini alma
Kullanıcıları zaten oluşturduysanız ve bu sayfadaki "Oda katılımcılarını ayarlama" bölümünden sonra odaya katılımcı olarak eklediyseniz, bu kullanıcıları doğrudan odaya katılmak için kullanabilirsiniz.
Aksi takdirde, her çağrı katılımcısı için bir Kullanıcı Erişim Belirteci oluşturmanız gerekir. Kullanıcı erişim belirteçleri oluşturmayı ve yönetmeyi öğrenin. Kullanıcı ve erişim belirteci oluşturmak için Azure CLI'yi kullanabilir ve bağlantı dizesi aşağıdaki komutu çalıştırabilirsiniz. Kullanıcılar oluşturulduktan sonra, odaya katılmadan önce onları katılımcı olarak odaya eklemeniz gerekir.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Ayrıntılar için bkz . Erişim Belirteçleri Oluşturmak ve Yönetmek için Azure CLI kullanma.
Not
Odalara Azure İletişim Hizmetleri Kullanıcı Arabirimi Kitaplığı kullanılarak erişilebilir. Kullanıcı Arabirimi Kitaplığı, geliştiricilerin uygulamalarına yalnızca birkaç kod satırıyla Odalar etkinleştirilmiş bir çağrı istemcisi eklemesine olanak tanır.
Oda aramasını katılma
Bu hızlı başlangıcı takip etmek için GitHub'da Oda Araması hızlı başlangıcını indirebilirsiniz.
Önkoşullar
- 18 Node.js olmalı. Msi yükleyicisini kullanarak yükleyebilirsiniz.
Ayarlama
Yeni bir Node.js uygulaması oluşturma
Terminalinizi veya komut pencerenizi açın, uygulamanız için yeni bir dizin oluşturun ve bu dizine gidin.
mkdir calling-rooms-quickstart && cd calling-rooms-quickstart
Varsayılan ayarlarla bir package.json dosyası oluşturmak için komutunu çalıştırınnpm init -y
.
npm init -y
paketini yükleyin
npm install
JavaScript için Azure İletişim Hizmetleri Çağırma SDK'sını yüklemek için komutunu kullanın.
Önemli
Bu hızlı başlangıçta Azure İletişim Hizmetleri Çağırma SDK'sı sürümü kullanılmaktadır1.14.1
. Oda aramasına katılma ve arama katılımcılarının rollerini görüntüleme olanağı, Web tarayıcıları için JavaScript SDK'sı 1.13.1 ve üzeri sürümlerde kullanılabilir.
npm install @azure/communication-common --save
npm install @azure/communication-calling@1.14.1 --save
Uygulama çerçevesini ayarlama
Bu hızlı başlangıçta uygulama varlıklarını paketlemek için Webpack kullanılır. aşağıdaki komutu çalıştırarak webpack
, webpack-cli
ve webpack-dev-server
npm paketlerini yükleyin ve bunları içinde geliştirme bağımlılıkları olarak listeleyin package.json
:
npm install copy-webpack-plugin@^11.0.0 webpack@^5.88.2 webpack-cli@^5.1.4 webpack-dev-server@^4.15.1 --save-dev
Kod şu şekildedir:
Projenizin kök dizininde bir index.html
dosya oluşturun. Bu dosyayı, kullanıcının oda çağrısına katılmasını sağlayan temel bir düzen yapılandırmak için kullanırız.
<!-- index.html-->
<!DOCTYPE html>
<html>
<head>
<title>Azure Communication Services - Rooms Call Sample</title>
<link rel="stylesheet" type="text/css" href="styles.css"/>
</head>
<body>
<h4>Azure Communication Services - Rooms Call Sample</h4>
<input id="user-access-token"
type="text"
placeholder="User access token"
style="margin-bottom:1em; width: 500px;"/>
<button id="initialize-call-agent" type="button">Initialize Call Agent</button>
<br>
<br>
<input id="acs-room-id"
type="text"
placeholder="Enter Room Id"
style="margin-bottom:1em; width: 500px; display: block;"/>
<button id="join-room-call-button" type="button" disabled="true">Join Room Call</button>
<button id="hangup-call-button" type="button" disabled="true">Hang up Call</button>
<button id="start-video-button" type="button" disabled="true">Start Video</button>
<button id="stop-video-button" type="button" disabled="true">Stop Video</button>
<br>
<br>
<div id="connectedLabel" style="color: #13bb13;" hidden>Room Call is connected!</div>
<br>
<div id="remoteVideosGallery" style="width: 40%;" hidden>Remote participants' video streams:</div>
<br>
<div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
<!-- points to the bundle generated from client.js -->
<script src="./main.js"></script>
</body>
</html>
Projenizin kök dizininde, bu hızlı başlangıcın uygulama mantığını içerecek şekilde adlı index.js
bir dosya oluşturun. index.js aşağıdaki kodu ekleyin:
// Make sure to install the necessary dependencies
const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential } = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the log level and output
setLogLevel('verbose');
AzureLogger.log = (...args) => {
console.log(...args);
};
// Calling web sdk objects
let callAgent;
let deviceManager;
let call;
let localVideoStream;
let localVideoStreamRenderer;
// UI widgets
let userAccessToken = document.getElementById('user-access-token');
let acsRoomId = document.getElementById('acs-room-id');
let initializeCallAgentButton = document.getElementById('initialize-call-agent');
let startCallButton = document.getElementById('join-room-call-button');
let hangUpCallButton = document.getElementById('hangup-call-button');
let startVideoButton = document.getElementById('start-video-button');
let stopVideoButton = document.getElementById('stop-video-button');
let connectedLabel = document.getElementById('connectedLabel');
let remoteVideosGallery = document.getElementById('remoteVideosGallery');
let localVideoContainer = document.getElementById('localVideoContainer');
/**
* Using the CallClient, initialize a CallAgent instance with a CommunicationUserCredential which enable us to join a rooms call.
*/
initializeCallAgentButton.onclick = async () => {
try {
const callClient = new CallClient();
tokenCredential = new AzureCommunicationTokenCredential(userAccessToken.value.trim());
callAgent = await callClient.createCallAgent(tokenCredential)
// Set up a camera device to use.
deviceManager = await callClient.getDeviceManager();
await deviceManager.askDevicePermission({ video: true });
await deviceManager.askDevicePermission({ audio: true });
startCallButton.disabled = false;
initializeCallAgentButton.disabled = true;
} catch(error) {
console.error(error);
}
}
startCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
const roomCallLocator = { roomId: acsRoomId.value.trim() };
call = callAgent.join(roomCallLocator, { videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
/**
* Subscribe to a call obj.
* Listen for property changes and collection updates.
*/
subscribeToCall = (call) => {
try {
// Inspect the initial call.id value.
console.log(`Call Id: ${call.id}`);
//Subscribe to call's 'idChanged' event for value changes.
call.on('idChanged', () => {
console.log(`Call Id changed: ${call.id}`);
});
// Inspect the initial call.state value.
console.log(`Call state: ${call.state}`);
// Subscribe to call's 'stateChanged' event for value changes.
call.on('stateChanged', async () => {
console.log(`Call state changed: ${call.state}`);
if(call.state === 'Connected') {
connectedLabel.hidden = false;
startCallButton.disabled = true;
hangUpCallButton.disabled = false;
startVideoButton.disabled = false;
stopVideoButton.disabled = false;
remoteVideosGallery.hidden = false;
} else if (call.state === 'Disconnected') {
connectedLabel.hidden = true;
startCallButton.disabled = false;
hangUpCallButton.disabled = true;
startVideoButton.disabled = true;
stopVideoButton.disabled = true;
remoteVideosGallery.hidden = true;
console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
}
});
call.on('isLocalVideoStartedChanged', () => {
console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
});
console.log(`isLocalVideoStarted: ${call.isLocalVideoStarted}`);
call.localVideoStreams.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
call.on('localVideoStreamsUpdated', e => {
e.added.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
e.removed.forEach(lvs => {
removeLocalVideoStream();
});
});
// Inspect the call's current remote participants and subscribe to them.
call.remoteParticipants.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant);
});
// Subscribe to the call's 'remoteParticipantsUpdated' event to be
// notified when new participants are added to the call or removed from the call.
call.on('remoteParticipantsUpdated', e => {
// Subscribe to new remote participants that are added to the call.
e.added.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant)
});
// Unsubscribe from participants that are removed from the call
e.removed.forEach(remoteParticipant => {
console.log('Remote participant removed from the call.');
});
});
} catch (error) {
console.error(error);
}
}
/**
* Subscribe to a remote participant obj.
* Listen for property changes and collection udpates.
*/
subscribeToRemoteParticipant = (remoteParticipant) => {
try {
// Inspect the initial remoteParticipant.state value.
console.log(`Remote participant state: ${remoteParticipant.state}`);
// Subscribe to remoteParticipant's 'stateChanged' event for value changes.
remoteParticipant.on('stateChanged', () => {
console.log(`Remote participant state changed: ${remoteParticipant.state}`);
});
// Inspect the remoteParticipants's current videoStreams and subscribe to them.
remoteParticipant.videoStreams.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
// notified when the remoteParticiapant adds new videoStreams and removes video streams.
remoteParticipant.on('videoStreamsUpdated', e => {
// Subscribe to new remote participant's video streams that were added.
e.added.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Unsubscribe from remote participant's video streams that were removed.
e.removed.forEach(remoteVideoStream => {
console.log('Remote participant video stream was removed.');
})
});
} catch (error) {
console.error(error);
}
}
/**
* Subscribe to a remote participant's remote video stream obj.
* You have to subscribe to the 'isAvailableChanged' event to render the remoteVideoStream. If the 'isAvailable' property
* changes to 'true', a remote participant is sending a stream. Whenever availability of a remote stream changes
* you can choose to destroy the whole 'Renderer', a specific 'RendererView' or keep them, but this will result in displaying blank video frame.
*/
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
let renderer = new VideoStreamRenderer(remoteVideoStream);
let view;
let remoteVideoContainer = document.createElement('div');
remoteVideoContainer.className = 'remote-video-container';
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);
}
}
}
/**
* Start your local video stream.
* This will send your local video stream to remote participants so they can view it.
*/
startVideoButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
await call.startVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
/**
* Stop your local video stream.
* This will stop your local video stream from being sent to remote participants.
*/
stopVideoButton.onclick = async () => {
try {
await call.stopVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
/**
* To render a LocalVideoStream, you need to create a new instance of VideoStreamRenderer, and then
* create a new VideoStreamRendererView instance using the asynchronous createView() method.
* You may then attach view.target to any UI element.
*/
createLocalVideoStream = async () => {
const camera = (await deviceManager.getCameras())[0];
if (camera) {
return new LocalVideoStream(camera);
} else {
console.error(`No camera device found on the system`);
}
}
/**
* Display your local video stream preview in your UI
*/
displayLocalVideoStream = async () => {
try {
localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await localVideoStreamRenderer.createView();
localVideoContainer.hidden = false;
localVideoContainer.appendChild(view.target);
} catch (error) {
console.error(error);
}
}
/**
* Remove your local video stream preview from your UI
*/
removeLocalVideoStream = async() => {
try {
localVideoStreamRenderer.dispose();
localVideoContainer.hidden = true;
} catch (error) {
console.error(error);
}
}
/**
* End current room call
*/
hangUpCallButton.addEventListener("click", async () => {
await call.hangUp();
});
Webpack yerel sunucu kodunu ekleme
Bu hızlı başlangıcın yerel sunucu mantığını içermesi için projenizin kök dizininde webpack.config.js adlı bir dosya oluşturun. webpack.config.js aşağıdaki kodu ekleyin:
const path = require('path');
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: {
directory: path.join(__dirname, './')
},
},
plugins: [
new CopyPlugin({
patterns: [
'./index.html'
]
}),
]
};
Kodu çalıştırma
Uygulamanızı derlemek ve çalıştırmak için öğesini webpack-dev-server
kullanın. Uygulama konağını yerel bir web sunucusu içinde paketlemek için aşağıdaki komutu çalıştırın:
`npx webpack serve --config webpack.config.js`
- Tarayıcınızı açın http://localhost:8080/.
- İlk giriş alanına geçerli bir kullanıcı erişim belirteci girin.
- "Çağrı Aracısını Başlat"a tıklayın ve Oda Kimliğinizi girin.
- "Oda Aramasını Katıl" seçeneğine tıklayın
Artık bir Rooms çağrısına başarıyla katıldınız!
Oda çağrısına katılmayı anlama
Hızlı Başlangıç uygulamanıza eklediğiniz tüm kodlar, bir oda çağrısını başarıyla başlatmanıza ve katılmanıza olanak sağladı. Burada, uygulamanızdaki işlevselliği genişletmek için Odalar için erişebileceğiniz diğer yöntemler/işleyiciler hakkında daha fazla bilgi bulabilirsiniz.
Yerel veya uzak çağrı katılımcılarının rolünü görüntülemek için aşağıdaki işleyiciye abone olun.
// Subscribe to changes for your role in a call
const callRoleChangedHandler = () => {
console.log(call.role);
};
call.on('roleChanged', callRoleChangedHandler);
// Subscribe to role changes for remote participants
const subscribeToRemoteParticipant = (remoteParticipant) => {
remoteParticipant.on('roleChanged', () => {
console.log(remoteParticipant.role);
});
}
Oda kavramı belgelerinde oda araması katılımcılarının rolleri hakkında daha fazla bilgi edinebilirsiniz.
Oda aramasını katılma
Bu hızlı başlangıcı takip etmek için GitHub'da Oda Araması hızlı başlangıcını indirebilirsiniz.
Ayarlama
Xcode projesi oluşturma
Xcode'da yeni bir iOS projesi oluşturun ve Tek Görünüm Uygulaması şablonunu seçin. Bu öğreticide SwiftUI çerçevesi kullanılır, bu nedenle Dili Swift olarak ve Kullanıcı Arabirimini SwiftUI olarak ayarlamanız gerekir.
CocoaPods'u yükleme
Mac bilgisayarınıza CocoaPods yüklemek için bu kılavuzu kullanın.
CocoaPods ile paketi ve bağımlılıkları yükleme
Uygulamanız için bir Podfile oluşturmak için terminali açın ve proje klasörüne gidin ve pod init komutunu çalıştırın.
Podfile'a aşağıdaki kodu ekleyin ve kaydedin:
platform :ios, '13.0'
use_frameworks!
target 'roomsquickstart' do
pod 'AzureCommunicationCalling', '~> 2.5.0'
end
Pod yüklemesini çalıştırın.
.xcworkspace
Dosyayı Xcode ile açın.
Mikrofona ve kameraya erişim isteme
Cihazın mikrofon ve kamerasına erişmek için, uygulamanızın Bilgi Özellik Listesi'ni ve NSCameraUsageDescription
ile NSMicrophoneUsageDescription
güncelleştirmeniz gerekir. İlişkili değeri, sistemin kullanıcıdan erişim istemek için kullandığı iletişim kutusuna eklenecek bir dizeye ayarlayın.
Proje ağacının girdisine Info.plist
sağ tıklayın ve Kaynak Kodu Olarak > Aç'ı seçin. Aşağıdaki satırları en üst düzey <dict>
bölüme ekleyin ve dosyayı kaydedin.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>
Uygulama çerçevesini ayarlama
Projenizin ContentView.swift
dosyasını açın ve ve kitaplığını AVFoundation
içeri aktarmak için dosyanın en üstüne bir içeri aktarma AzureCommunicationCalling
bildirimi ekleyin. AVFoundation, koddan ses izni yakalamak için kullanılır.
import AzureCommunicationCalling
import AVFoundation
Nesne modeli
Aşağıdaki sınıflar ve arabirimler, iOS için Azure İletişim Hizmetleri Çağırma SDK'sının bazı önemli özelliklerini işler.
Veri Akışı Adı | Açıklama |
---|---|
CallClient | CallClient, Çağrı SDK'sının ana giriş noktasıdır. |
CallAgent | CallAgent, çağrıları başlatmak ve yönetmek için kullanılır. |
CommunicationTokenCredential | CommunicationTokenCredential, CallAgent örneğini başlatmak için belirteç kimlik bilgisi olarak kullanılır. |
CommunicationIdentifier | CommunicationIdentifier, kullanıcının kimliğini temsil etmek için kullanılır ve şu değerlerden birine sahip olabilir: CommunicationUserIdentifier/Telefon NumberIdentifier/CallingApplication. |
RoomCallLocator | RoomCallLocator, CallAgent tarafından bir Oda çağrısına katılmak için kullanılır |
Arama Aracısı oluşturma
ContentView yapısının uygulamasını, kullanıcının çağrı başlatmasını ve sonlandırmasını sağlayan bazı basit kullanıcı arabirimi denetimleriyle değiştirin. Bu hızlı başlangıçta bu denetimlere iş mantığı ekleyeceğiz.
struct ContentView: View {
@State var roomId: String = ""
@State var callObserver:CallObserver?
@State var previewRenderer: VideoStreamRenderer? = nil
@State var previewView: RendererView? = nil
@State var sendingLocalVideo: Bool = false
@State var speakerEnabled: Bool = false
@State var muted: Bool = false
@State var callClient: CallClient?
@State var call: Call?
@State var callHandler: CallHandler?
@State var callAgent: CallAgent?
@State var deviceManager: DeviceManager?
@State var localVideoStreams: [LocalVideoStream]?
@State var callState: String = "Unknown"
@State var showAlert: Bool = false
@State var alertMessage: String = ""
@State var participants: [[Participant]] = [[]]
var body: some View {
NavigationView {
ZStack {
if (call == nil) {
Form {
Section {
TextField("Room ID", text: $roomId)
Button(action: joinRoomCall) {
Text("Join Room Call")
}
}
}
.navigationBarTitle("Rooms Quickstart")
} else {
ZStack {
VStack {
ForEach(participants, id:\.self) { array in
HStack {
ForEach(array, id:\.self) { participant in
ParticipantView(self, participant)
}
}
.frame(maxWidth: .infinity, maxHeight: 200, alignment: .topLeading)
}
}
.background(Color.black)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
VStack {
if (sendingLocalVideo) {
HStack {
RenderInboundVideoView(view: $previewView)
.frame(width:90, height:160)
.padding(10)
.background(Color.green)
}
.frame(maxWidth: .infinity, alignment: .trailing)
}
HStack {
Button(action: toggleMute) {
HStack {
Text(muted ? "Unmute" : "Mute")
}
.frame(width:80)
.padding(.vertical, 10)
.background(Color(.lightGray))
}
Button(action: toggleLocalVideo) {
HStack {
Text(sendingLocalVideo ? "Video-Off" : "Video-On")
}
.frame(width:80)
.padding(.vertical, 10)
.background(Color(.lightGray))
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 10)
.padding(.vertical, 5)
HStack {
Button(action: leaveRoomCall) {
HStack {
Text("Leave Room Call")
}
.frame(width:80)
.padding(.vertical, 10)
.background(Color(.red))
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 10)
.padding(.vertical, 5)
HStack {
Text("Status:")
Text(callState)
}
.padding(.vertical, 10)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading)
}
}
}
}
.onAppear{
// Authenticate the client
// Initialize the CallAgent and access Device Manager
// Ask for permissions
}
}
}
//Functions and Observers
struct HomePageView_Previews: PreviewProvider {
static var previews: some View {
HomePageView()
}
}
İstemcinin kimliğini doğrulama
CallAgent örneğini başlatmak için Oda çağrılarına katılmamızı sağlayacak bir Kullanıcı Erişim Belirteci gerekir.
Belirteciniz olduktan sonra içindeki geri ContentView.swift
çağırmaya onAppear
aşağıdaki kodu ekleyin. değerini kaynağınız için geçerli bir kullanıcı erişim belirteci ile değiştirmeniz <USER ACCESS TOKEN>
gerekir:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
CallAgent'ı başlatın ve Aygıt Yöneticisi erişin
Bir CallClient'dan CallAgent örneği oluşturmak için, başlatıldıktan sonra zaman uyumsuz olarak bir CallAgent nesnesi döndüren yöntemini kullanın callClient.createCallAgent
. DeviceManager, ses/video akışlarını iletmek için bir çağrıda kullanılabilecek yerel cihazları listelemenize olanak tanır. Ayrıca bir kullanıcıdan mikrofona/kameraya erişim izni istemenizi sağlar.
self.callClient = CallClient()
self.callClient?.createCallAgent(userCredential: userCredential!) { (agent, error) in
if error != nil {
print("ERROR: It was not possible to create a call agent.")
return
} else {
self.callAgent = agent
print("Call agent successfully created.")
self.callAgent!.delegate = callHandler
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")
}
}
}
}
İzin iste
Ses ve görüntü izinlerini istemek için geri çağırmaya aşağıdaki kodu onAppear
eklememiz gerekir.
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
/* NO OPERATION */
}
}
}
Oda çağrısına katılma
yöntemi, joinRoomCall
Oda Çağrısına Katıl düğmesine dokunulduğunda gerçekleştirilecek eylem olarak ayarlanır. Bu hızlı başlangıçta, aramalar yalnızca varsayılan olarak seslidir ancak oda katıldığında video etkinleştirilebilir.
func joinRoomCall() {
if self.callAgent == nil {
print("CallAgent not initialized")
return
}
if (self.roomId.isEmpty) {
print("Room ID not set")
return
}
// Join a call with a Room ID
let options = JoinCallOptions()
let audioOptions = AudioOptions()
audioOptions.muted = self.muted
options.audioOptions = audioOptions
let roomCallLocator = RoomCallLocator(roomId: roomId)
self.callAgent!.join(with: roomCallLocator, joinCallOptions: options) { (call, error) in
self.setCallAndObserver(call: call, error: error)
}
}
CallObserver
, arama ortası olaylarını ve uzak katılımcıları yönetmek için kullanılır. İşlevdeki setCallAndOberserver
gözlemcileri ayarlayacağız.
func setCallAndObserver(call:Call!, error:Error?) {
if (error == nil) {
self.call = call
self.callObserver = CallObserver(view:self)
self.call!.delegate = self.callObserver
if (self.call!.state == CallState.connected) {
self.callObserver!.handleInitialCallState(call: call)
}
} else {
print("Failed to get call object")
}
}
Oda aramasını bırakma
yöntemi, leaveRoomCall
Oda Aramasını Bırak düğmesine dokunulduğunda gerçekleştirilecek eylem olarak ayarlanır. Çağrıdan ayrılmayı işler ve oluşturulan tüm kaynakları temizler.
private func leaveRoomCall() {
if (self.sendingLocalVideo) {
self.call!.stopVideo(stream: self.localVideoStreams!.first!) { (error) in
if (error != nil) {
print("Failed to stop video")
} else {
self.sendingLocalVideo = false
self.previewView = nil
self.previewRenderer?.dispose()
self.previewRenderer = nil
}
}
}
self.call?.hangUp(options: nil) { (error) in }
self.participants.removeAll()
self.call?.delegate = nil
self.call = nil
}
Video yayınlama
Oda araması sırasında, uzak katılımcılara göndermeyi LocalVideoStream
başlatmak veya durdurmak için veya kullanabiliriz.startVideo
stopVideo
func toggleLocalVideo() {
if (self.sendingLocalVideo) {
self.call!.stopVideo(stream: self.localVideoStreams!.first!) { (error) in
if (error != nil) {
print("Cannot stop video")
} else {
self.sendingLocalVideo = false
self.previewView = nil
self.previewRenderer!.dispose()
self.previewRenderer = nil
}
}
} else {
let availableCameras = self.deviceManager!.cameras
let scalingMode:ScalingMode = .crop
if (self.localVideoStreams == nil) {
self.localVideoStreams = [LocalVideoStream]()
}
self.localVideoStreams!.append(LocalVideoStream(camera: availableCameras.first!))
self.previewRenderer = try! VideoStreamRenderer(localVideoStream: self.localVideoStreams!.first!)
self.previewView = try! previewRenderer!.createView(withOptions: CreateViewOptions(scalingMode:scalingMode))
self.call!.startVideo(stream: self.localVideoStreams!.first!) { (error) in
if (error != nil) {
print("Cannot start video")
}
else {
self.sendingLocalVideo = true
}
}
}
}
Yerel sesi kapatma
Oda araması sırasında mikrofonumuzun sesini kapatmak veya unMute
açmak için kullanabilirizmute
.
func toggleMute() {
if (self.muted) {
call!.unmuteOutgoingAudio(completionHandler: { (error) in
if error == nil {
self.muted = false
}
})
} else {
call!.muteOutgoingAudio(completionHandler: { (error) in
if error == nil {
self.muted = true
}
})
}
}
Arama güncelleştirmelerini işleme
Arama güncelleştirmeleriyle başa çıkmak için güncelleştirme olaylarını işlemek için bir CallHandler
uygulayın. Aşağıdaki uygulamayı içine CallHandler.swift
yerleştirin.
final class CallHandler: NSObject, CallAgentDelegate {
public var owner: ContentView?
private static var instance: CallHandler?
static func getOrCreateInstance() -> CallHandler {
if let c = instance {
return c
}
instance = CallHandler()
return instance!
}
private override init() {}
public func callAgent(_ callAgent: CallAgent, didUpdateCalls args: CallsUpdatedEventArgs) {
if let removedCall = args.removedCalls.first {
owner?.call = nil
}
}
}
içindeki geri ContentView.swift
çağırmaya aşağıdaki kodu onAppear
ekleyerek örneğini CallHandler
oluşturmamız gerekir:
self.callHandler = CallHandler.getOrCreateInstance()
self.callHandler.owner = self
CallAgent başarıyla oluşturulduktan sonra CallAgent için bir temsilci ayarlayın:
self.callAgent!.delegate = callHandler
Uzaktan katılımcı yönetimi
Tüm uzak katılımcılar türüyle RemoteParticipant
temsil edilir ve bir çağrı örneğindeki remoteParticipants
koleksiyon aracılığıyla kullanılabilir. Uzak katılımcıların uzak video akışlarında güncelleştirmeleri yönetmek için bir Participant
sınıf uygulayabiliriz.
class Participant: NSObject, RemoteParticipantDelegate, ObservableObject {
private var videoStreamCount = 0
private let innerParticipant:RemoteParticipant
private let call:Call
private var renderedRemoteVideoStream:RemoteVideoStream?
@Published var state:ParticipantState = ParticipantState.disconnected
@Published var isMuted:Bool = false
@Published var isSpeaking:Bool = false
@Published var hasVideo:Bool = false
@Published var displayName:String = ""
@Published var videoOn:Bool = true
@Published var renderer:VideoStreamRenderer? = nil
@Published var rendererView:RendererView? = nil
@Published var scalingMode: ScalingMode = .fit
init(_ call: Call, _ innerParticipant: RemoteParticipant) {
self.call = call
self.innerParticipant = innerParticipant
self.displayName = innerParticipant.displayName
super.init()
self.innerParticipant.delegate = self
self.state = innerParticipant.state
self.isMuted = innerParticipant.isMuted
self.isSpeaking = innerParticipant.isSpeaking
self.hasVideo = innerParticipant.videoStreams.count > 0
if(self.hasVideo) {
handleInitialRemoteVideo()
}
}
deinit {
self.innerParticipant.delegate = nil
}
func getMri() -> String {
Utilities.toMri(innerParticipant.identifier)
}
func set(scalingMode: ScalingMode) {
if self.rendererView != nil {
self.rendererView!.update(scalingMode: scalingMode)
}
self.scalingMode = scalingMode
}
func handleInitialRemoteVideo() {
renderedRemoteVideoStream = innerParticipant.videoStreams[0]
renderer = try! VideoStreamRenderer(remoteVideoStream: renderedRemoteVideoStream!)
rendererView = try! renderer!.createView()
}
func toggleVideo() {
if videoOn {
rendererView = nil
renderer?.dispose()
videoOn = false
}
else {
renderer = try! VideoStreamRenderer(remoteVideoStream: innerParticipant.videoStreams[0])
rendererView = try! renderer!.createView()
videoOn = true
}
}
func remoteParticipant(_ remoteParticipant: RemoteParticipant, didUpdateVideoStreams args: RemoteVideoStreamsEventArgs) {
let hadVideo = hasVideo
hasVideo = innerParticipant.videoStreams.count > 0
if videoOn {
if hadVideo && !hasVideo {
// Remote user stopped sharing
rendererView = nil
renderer?.dispose()
} else if hasVideo && !hadVideo {
// remote user started sharing
renderedRemoteVideoStream = innerParticipant.videoStreams[0]
renderer = try! VideoStreamRenderer(remoteVideoStream: renderedRemoteVideoStream!)
rendererView = try! renderer!.createView()
} else if hadVideo && hasVideo {
if args.addedRemoteVideoStreams.count > 0 {
if renderedRemoteVideoStream?.id == args.addedRemoteVideoStreams[0].id {
return
}
// remote user added a second video, so switch to the latest one
guard let rendererTemp = renderer else {
return
}
rendererTemp.dispose()
renderedRemoteVideoStream = args.addedRemoteVideoStreams[0]
renderer = try! VideoStreamRenderer(remoteVideoStream: renderedRemoteVideoStream!)
rendererView = try! renderer!.createView()
} else if args.removedRemoteVideoStreams.count > 0 {
if args.removedRemoteVideoStreams[0].id == renderedRemoteVideoStream!.id {
// remote user stopped sharing video that we were rendering but is sharing
// another video that we can render
renderer!.dispose()
renderedRemoteVideoStream = innerParticipant.videoStreams[0]
renderer = try! VideoStreamRenderer(remoteVideoStream: renderedRemoteVideoStream!)
rendererView = try! renderer!.createView()
}
}
}
}
}
func remoteParticipant(_ remoteParticipant: RemoteParticipant, didChangeDisplayName args: PropertyChangedEventArgs) {
self.displayName = innerParticipant.displayName
}
}
class Utilities {
@available(*, unavailable) private init() {}
public static func toMri(_ id: CommunicationIdentifier?) -> String {
if id is CommunicationUserIdentifier {
let communicationUserIdentifier = id as! CommunicationUserIdentifier
return communicationUserIdentifier.identifier
} else {
return "<nil>"
}
}
}
Uzak katılımcı video akışları
Uzak katılımcıların video akışlarının işlenmesini işlemek için bir ParticipantView
oluşturabiliriz. Uygulamayı içine yerleştirme ParticipantView.swift
struct ParticipantView : View, Hashable {
static func == (lhs: ParticipantView, rhs: ParticipantView) -> Bool {
return lhs.participant.getMri() == rhs.participant.getMri()
}
private let owner: HomePageView
@State var showPopUp: Bool = false
@State var videoHeight = CGFloat(200)
@ObservedObject private var participant:Participant
var body: some View {
ZStack {
if (participant.rendererView != nil) {
HStack {
RenderInboundVideoView(view: $participant.rendererView)
}
.background(Color(.black))
.frame(height: videoHeight)
.animation(Animation.default)
} else {
HStack {
Text("No incoming video")
}
.background(Color(.red))
.frame(height: videoHeight)
}
}
}
func hash(into hasher: inout Hasher) {
hasher.combine(participant.getMri())
}
init(_ owner: HomePageView, _ participant: Participant) {
self.owner = owner
self.participant = participant
}
func resizeVideo() {
videoHeight = videoHeight == 200 ? 150 : 200
}
func showAlert(_ title: String, _ message: String) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.owner.alertMessage = message
self.owner.showAlert = true
}
}
}
struct RenderInboundVideoView: UIViewRepresentable {
@Binding var view:RendererView!
func makeUIView(context: Context) -> UIView {
return UIView()
}
func updateUIView(_ uiView: UIView, context: Context) {
for view in uiView.subviews {
view.removeFromSuperview()
}
if (view != nil) {
uiView.addSubview(view)
}
}
}
Olaylara Abone Olma
gibi remoteParticipants
değerler çağrı sırasında değiştiğinde bildirim almak üzere bir olay koleksiyonuna abone olmak için bir sınıf uygulayabilirizCallObserver
.
public class CallObserver : NSObject, CallDelegate
{
private var owner: ContentView
private var firstTimeCallConnected: Bool = true
init(view: ContentView) {
owner = view
super.init()
}
public func call(_ call: Call, didChangeState args: PropertyChangedEventArgs) {
let state = CallObserver.callStateToString(state:call.state)
owner.callState = state
if (call.state == CallState.disconnected) {
owner.leaveRoomCall()
}
else if (call.state == CallState.connected) {
if(self.firstTimeCallConnected) {
self.handleInitialCallState(call: call);
}
self.firstTimeCallConnected = false;
}
}
public func handleInitialCallState(call: Call) {
// We want to build a matrix with max 2 columns
owner.callState = CallObserver.callStateToString(state:call.state)
var participants = [Participant]()
// Add older/existing participants
owner.participants.forEach { (existingParticipants: [Participant]) in
participants.append(contentsOf: existingParticipants)
}
owner.participants.removeAll()
// Add new participants to the collection
for remoteParticipant in call.remoteParticipants {
let mri = Utilities.toMri(remoteParticipant.identifier)
let found = participants.contains { (participant) -> Bool in
participant.getMri() == mri
}
if !found {
let participant = Participant(call, remoteParticipant)
participants.append(participant)
}
}
// Convert 1-D array into a 2-D array with 2 columns
var indexOfParticipant = 0
while indexOfParticipant < participants.count {
var newParticipants = [Participant]()
newParticipants.append(participants[indexOfParticipant])
indexOfParticipant += 1
if (indexOfParticipant < participants.count) {
newParticipants.append(participants[indexOfParticipant])
indexOfParticipant += 1
}
owner.participants.append(newParticipants)
}
}
public func call(_ call: Call, didUpdateRemoteParticipant args: ParticipantsUpdatedEventArgs) {
var participants = [Participant]()
// Add older/existing participants
owner.participants.forEach { (existingParticipants: [Participant]) in
participants.append(contentsOf: existingParticipants)
}
owner.participants.removeAll()
// Remove deleted participants from the collection
args.removedParticipants.forEach { p in
let mri = Utilities.toMri(p.identifier)
participants.removeAll { (participant) -> Bool in
participant.getMri() == mri
}
}
// Add new participants to the collection
for remoteParticipant in args.addedParticipants {
let mri = Utilities.toMri(remoteParticipant.identifier)
let found = participants.contains { (view) -> Bool in
view.getMri() == mri
}
if !found {
let participant = Participant(call, remoteParticipant)
participants.append(participant)
}
}
// Convert 1-D array into a 2-D array with 2 columns
var indexOfParticipant = 0
while indexOfParticipant < participants.count {
var array = [Participant]()
array.append(participants[indexOfParticipant])
indexOfParticipant += 1
if (indexOfParticipant < participants.count) {
array.append(participants[indexOfParticipant])
indexOfParticipant += 1
}
owner.participants.append(array)
}
}
private static func callStateToString(state:CallState) -> String {
switch state {
case .connected: return "Connected"
case .connecting: return "Connecting"
case .disconnected: return "Disconnected"
case .disconnecting: return "Disconnecting"
case .none: return "None"
default: return "Unknown"
}
}
}
Kodu çalıştırma
Ürün > Çalıştır'ı seçerek veya (⌘-R) klavye kısayolunu kullanarak uygulamanızı iOS simülatöründe derleyebilir ve çalıştırabilirsiniz.
Bir oda çağrısına katılma ve arama katılımcılarının rollerini görüntüleme özelliği, iOS Mobil Arama SDK'sı sürüm 2.5.0 ve üzeri sürümlerde kullanılabilir.
Oda kavramı belgelerinde oda araması katılımcılarının rolleri hakkında daha fazla bilgi edinebilirsiniz.
Örnek uygulama
Bu hızlı başlangıcı takip etmek için GitHub'da Oda Araması hızlı başlangıcını indirebilirsiniz.
Projeyi ayarlama
Boş etkinlik içeren bir Android uygulaması oluşturma
Android Studio'dan yeni bir proje oluşturun:
Projenize Oda Çağrısı Hızlı Başlangıç adını verin ve Kotlin'i seçin.
paketini yükleyin
Modül düzeyinizde build.gradle
bölümüne aşağıdaki satırı dependencies
ekleyin.
dependencies {
...
//Ability to join a Rooms calls is available in 2.4.0 or above.
implementation 'com.azure.android:azure-communication-calling:2.4.0'
...
}
Uygulama bildirimine izin ekleme
Çağrı yapmak için gerekli izinleri istemek için, önce uygulama bildiriminde (app/src/main/AndroidManifest.xml
) izinleri bildirmeniz gerekir. Aşağıdakileri bildirim dosyanıza kopyalayın:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppTheme">
<!--Our Calling SDK depends on the Apache HTTP SDK.
When targeting Android SDK 28+, this library needs to be explicitly referenced.
See https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Uygulamanın düzenini ayarlama
Oda kimliği için bir metin girişi, aramayı yerleştirmek için bir düğme ve aramayı kapatmaya yönelik ek düğme gerekir.
adresine app/src/main/res/layout/activity_main.xml
gidin ve dosyasının içeriğini aşağıdaki kodla değiştirin:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text_role"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Role:"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/text_call_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Call Status"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="48dp" />
<EditText
android:id="@+id/room_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Room ID"
android:inputType="textPersonName"
android:layout_marginTop="100dp"
android:layout_marginHorizontal="20dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="260dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/call_button"
android:layout_width="wrap_content"
android:layout_marginEnd="32dp"
android:layout_height="wrap_content"
android:text="Start Call" />
<Button
android:id="@+id/hangup_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hangup" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Ana etkinliği oluşturma
Düzen oluşturulduktan sonra, Oda çağrısı başlatmak için mantığı ekleyebilirsiniz. Etkinlik çalışma zamanı izinleri isteme, çağrı aracısını oluşturma ve düğmeye basıldığında çağrıyı yerleştirme işlemlerini işler.
onCreate
yöntemi ve createAgent
öğesini çağırır getAllPermissions
ve çağrı düğmesi için bağlamaları ekler.
Bu olay, etkinlik oluşturulduğunda yalnızca bir kez gerçekleşir. hakkında onCreate
daha fazla bilgi için etkinlik yaşam döngüsünü anlama kılavuzuna bakın.
MainActivity.kt dosyasına gidin ve içeriği aşağıdaki kodla değiştirin:
package com.contoso.roomscallquickstart
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.media.AudioManager
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.azure.android.communication.calling.Call
import com.azure.android.communication.calling.CallAgent
import com.azure.android.communication.calling.CallClient
import com.azure.android.communication.calling.HangUpOptions
import com.azure.android.communication.calling.JoinCallOptions
import com.azure.android.communication.calling.RoomCallLocator
import com.azure.android.communication.common.CommunicationTokenCredential
import java.util.concurrent.ExecutionException
class MainActivity : AppCompatActivity() {
private val allPermissions = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE
)
private val userToken = "<ACS_USER_TOKEN>"
private lateinit var callAgent: CallAgent
private var call: Call? = null
private lateinit var roleTextView: TextView
private lateinit var statusView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getAllPermissions()
createCallAgent()
val callButton: Button = findViewById(R.id.call_button)
callButton.setOnClickListener { startCall() }
val hangupButton: Button = findViewById(R.id.hangup_button)
hangupButton.setOnClickListener { endCall() }
roleTextView = findViewById(R.id.text_role)
statusView = findViewById(R.id.text_call_status)
volumeControlStream = AudioManager.STREAM_VOICE_CALL
}
/**
* Start a call
*/
private fun startCall() {
if (userToken.startsWith("<")) {
Toast.makeText(this, "Please enter token in source code", Toast.LENGTH_SHORT).show()
return
}
val roomIdView: EditText = findViewById(R.id.room_id)
val roomId = roomIdView.text.toString()
if (roomId.isEmpty()) {
Toast.makeText(this, "Please enter room ID", Toast.LENGTH_SHORT).show()
return
}
val joinCallOptions = JoinCallOptions()
val roomCallLocator = RoomCallLocator(roomId)
call = callAgent.join(applicationContext, roomCallLocator, joinCallOptions)
call?.addOnStateChangedListener { setCallStatus(call?.state.toString()) }
call?.addOnRoleChangedListener { setRoleText(call?.callParticipantRole.toString()) }
}
/**
* Ends the call previously started
*/
private fun endCall() {
try {
call?.hangUp(HangUpOptions())?.get()
} catch (e: ExecutionException) {
Toast.makeText(this, "Unable to hang up call", Toast.LENGTH_SHORT).show()
}
}
/**
* Create the call callAgent
*/
private fun createCallAgent() {
try {
val credential = CommunicationTokenCredential(userToken)
callAgent = CallClient().createCallAgent(applicationContext, credential).get()
} catch (ex: Exception) {
Toast.makeText(
applicationContext,
"Failed to create call callAgent.",
Toast.LENGTH_SHORT
).show()
}
}
/**
* Request each required permission if the app doesn't already have it.
*/
private fun getAllPermissions() {
val permissionsToAskFor = mutableListOf<String>()
for (permission in allPermissions) {
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsToAskFor.add(permission)
}
}
if (permissionsToAskFor.isNotEmpty()) {
ActivityCompat.requestPermissions(this, permissionsToAskFor.toTypedArray(), 1)
}
}
/**
* Ensure all permissions were granted, otherwise inform the user permissions are missing.
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
var allPermissionsGranted = true
for (result in grantResults) {
allPermissionsGranted = allPermissionsGranted && (result == PackageManager.PERMISSION_GRANTED)
}
if (!allPermissionsGranted) {
Toast.makeText(this, "All permissions are needed to make the call.", Toast.LENGTH_LONG).show()
finish()
}
}
@SuppressLint("SetTextI18n")
private fun setCallStatus(status: String?) {
runOnUiThread {
statusView.text = "Call Status: $status"
}
}
@SuppressLint("SetTextI18n")
private fun setRoleText(role: String?) {
runOnUiThread {
roleTextView.text = "Role: $role"
}
}
}
Not
Uygulamanızı tasarlarken bu izinlerin ne zaman istenmesi gerektiğini göz önünde bulundurun. İzinler önceden değil, gerektiğinde istenmelidir. Daha fazla bilgi için bkz . Android İzinleri Kılavuzu.
Projenizi çalıştırma
Projenizi çalıştırmadan önce değerini Azure İletişim Hizmetleri Kullanıcı Erişim Belirtecinizle değiştirin <ACS_USER_TOKEN>
MainActivity.kt
.
private val userToken = "<ACS_USER_TOKEN>"
Projeyi öykünücüde veya fiziksel bir cihazda çalıştırın.
Oda Kimliğinizi girmek için bir alan ve Oda Aramasını başlatmak için bir düğme görmeniz gerekir. Oda Kimliğinizi girin ve Rolünüzün yanı sıra Arama durumunun değiştiğini doğrulayın.
Oda çağrısına katılmayı anlama
Hızlı Başlangıç uygulamanıza eklediğiniz tüm kodlar, bir oda çağrısını başarıyla başlatmanıza ve katılmanıza olanak sağladı. Tüm bunların nasıl çalıştığına ve Odalar için erişebileceğiniz diğer yöntemlere/işleyicilere derinlemesine bakmamız gerekir.
Geçerli bir kullanıcı belirteci ile oluşturulan oda çağrıları birleştirilir CallAgent
:
private fun createCallAgent() {
try {
val credential = CommunicationTokenCredential(userToken)
callAgent = CallClient().createCallAgent(applicationContext, credential).get()
} catch (ex: Exception) {
Toast.makeText(
applicationContext,
"Failed to create call callAgent.",
Toast.LENGTH_SHORT
).show()
}
}
ve RoomCallLocator
kullanarakCallAgent
, nesnesini döndüren yöntemini kullanarak bir Call
oda çağrısına CallAgent.join
katılabiliriz:
val joinCallOptions = JoinCallOptions()
val roomCallLocator = RoomCallLocator(roomId)
call = callAgent.join(applicationContext, roomCallLocator, joinCallOptions)
Dosyanın ötesinde daha fazla özelleştirme, MainActivity.kt
güncelleştirmeleri almak için olaylara abone olmak Call
içerir:
call.addOnRemoteParticipantsUpdatedListener { args: ParticipantsUpdatedEvent? ->
handleRemoteParticipantsUpdate(
args!!
)
}
call.addOnStateChangedListener { args: PropertyChangedEvent? ->
this.handleCallOnStateChanged(
args!!
)
}
Aşağıdaki yöntemleri ve işleyicileri kullanarak yerel veya uzak çağrı katılımcılarının rolünü görüntülemek için daha fazla genişletebilirsiniz MainActivity.kt
.
// Get your role in the call
call.getCallParticipantRole();
// Subscribe to changes for your role in a call
private void isCallRoleChanged(PropertyChangedEvent propertyChangedEvent) {
// handle self-role change
}
call.addOnRoleChangedListener(isCallRoleChanged);
// Subscribe to role changes for remote participants
private void isRoleChanged(PropertyChangedEvent propertyChangedEvent) {
// handle remote participant role change
}
remoteParticipant.addOnRoleChangedListener(isRoleChanged);
// Get role of the remote participant
remoteParticipant.getCallParticipantRole();
Bir oda aramasına katılma ve arama katılımcılarının rollerini görüntüleme özelliği Android Mobil Arama SDK'sı 2.4.0 ve üzeri sürümlerde kullanılabilir.
Oda kavramı belgelerinde oda araması katılımcılarının rolleri hakkında daha fazla bilgi edinebilirsiniz.
Oda aramasını katılma
Oda aramasına katılmak için İstemci uygulamanıza görüntülü arama ekleme kılavuzunu kullanarak windows uygulamanızı ayarlayın. Alternatif olarak, GitHub'da görüntülü arama hızlı başlangıcını indirebilirsiniz.
Geçerli bir kullanıcı belirteci ile oluşturma callAgent
:
var creds = new CallTokenCredential("<user-token>");
CallAgentOptions callAgentOptions = new CallAgentOptions();
callAgentOptions.DisplayName = "<display-name>";
callAgent = await callClient.CreateCallAgentAsync(creds, callAgentOptions);
callAgent
bir oda çağrısına katılmak için ve RoomCallLocator
kullanın, CallAgent.JoinAsync
yöntemi bir CommunicationCall
nesne döndürür:
RoomCallLocator roomCallLocator = new RoomCallLocator('<RoomId>');
CommunicationCall communicationCall = await callAgent.JoinAsync(roomCallLocator, joinCallOptions);
Güncelleştirmeleri almak için CommunicationCall
olaylara abone olun:
private async void CommunicationCall_OnStateChanged(object sender, PropertyChangedEventArgs args) {
var call = sender as CommunicationCall;
if (sender != null)
{
switch (call.State){
// Handle changes in call state
}
}
}
Arama katılımcılarının rolünü görüntülemek için rol değişikliklerine abone olun:
private void RemoteParticipant_OnRoleChanged(object sender, Azure.Communication.Calling.WindowsClient.PropertyChangedEventArgs args)
{
_ = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
System.Diagnostics.Trace.WriteLine("Raising Role change, new Role: " + remoteParticipant_.Role);
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("RemoteParticipantRole"));
});
}
Bir oda çağrısına katılma ve arama katılımcılarının rollerini görüntüleme özelliği Windows NuGet Sürüm 1.1.0 ve üzeri sürümlerde kullanılabilir.
Oda kavramı belgelerinde oda araması katılımcılarının rolleri hakkında daha fazla bilgi edinebilirsiniz.
Sonraki adımlar
Bu bölümde şunların nasıl yapılacağını öğrendiniz:
- Uygulamanıza görüntülü arama ekleme
- Oda tanımlayıcısını çağrı SDK'sına geçirme
- Uygulamanızdan oda çağrısına katılma
Ayrıca şunları da isteyebilirsiniz:
- Oda kavramı hakkında bilgi edinin
- Sesli ve görüntülü arama kavramları hakkında bilgi edinin
- Kimlik doğrulama kavramları hakkında bilgi edinin
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin