ملاحظة
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تسجيل الدخول أو تغيير الدلائل.
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تغيير الدلائل.
تعرف على كيفية إدارة مكالمات الفيديو باستخدام Azure Communication Services SDKS. سنتعلم كيفية إدارة تلقي الفيديو وإرساله في مكالمة.
المتطلبات الأساسية
- حساب Azure مع اشتراك نشط. أنشئ حساباً مجاناً.
- تم نشر مورد Communication Services. إنشاء مورد Communication Services
- رمز وصول المستخدم لتمكين العميل المتصل. لمزيد من المعلومات، راجع إنشاء رموز الوصول المميزة وإدارتها.
- اختياري: أكمل التشغيل السريع لإضافة مكالمات صوتية إلى تطبيقك
تثبيت حزمة SDK
npm install
استخدم الأمر لتثبيت Azure Communication Services Common و Calling SDK ل JavaScript:
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()
إدارة اتصال SDK بالبنية الأساسية ل Microsoft
يساعدك المثيل Call Agent
على إدارة المكالمات (للانضمام إلى المكالمات أو بدء تشغيلها). من أجل العمل، تحتاج SDK للاتصال بالبنية الأساسية ل Microsoft للحصول على إعلامات بالمكالمات الواردة وتنسيق تفاصيل المكالمات الأخرى. لديك Call Agent
اثنتان من الحالات المحتملة:
متصل - Call Agent
قيمة connectionStatue تعني Connected
أن SDK العميل متصل وقادر على تلقي الإعلامات من البنية الأساسية ل Microsoft.
قطع الاتصال - Call Agent
قيمة Disconnected
connectionStatue من الحالات هناك مشكلة تمنع SDK من الاتصال بشكل صحيح.
Call Agent
يجب إعادة إنشائها.
-
invalidToken
: إذا انتهت صلاحية رمز مميز أو كان مثيل غير صالحCall Agent
يتم قطع اتصاله بهذا الخطأ. -
connectionIssue
: إذا كانت هناك مشكلة في اتصال العميل بالبنية الأساسية ل Microsoft، بعد أن تكشف العديد من عمليات إعادة المحاولةCall Agent
عنconnectionIssue
الخطأ.
يمكنك التحقق مما إذا كان محليك Call Agent
متصلا بالبنية الأساسية ل Microsoft عن طريق فحص القيمة الحالية للخاصية connectionState
. أثناء إجراء مكالمة نشطة، يمكنك الاستماع إلى 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) ويتوقف الميكروفون عن العمل اعتمادا على المستعرض ونظام التشغيل.
دفق الفيديو المحلي
لكي يرسل المستخدمون فيديو في مكالمة، يجب إنشاء كائن 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، قم بإزالة طريقة العرض من شجرة html عن طريق استدعاء removeChild()
الأسلوب من عقدة DOM التي تحتوي على المعاينة الخاصة بك.
// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);
طلب إذن الكاميرا والميكروفون
لا يمكن للتطبيق استخدام الكاميرا أو الميكروفون دون أذونات. يمكنك استخدام deviceManager لمطالبة المستخدم بمنح أذونات الكاميرا و/أو الميكروفون:
const result = await deviceManager.askDevicePermission({audio: true, video: true});
بمجرد حل الوعد، يرجع الأسلوب مع كائن 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.
إجراء مكالمة باستخدام كاميرا الفيديو
هام
حاليا يتم دعم دفق فيديو محلي صادر واحد فقط.
لوضع مكالمة فيديو، يجب عليك تعداد الكاميرات المحلية باستخدام 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()
واجهة برمجة التطبيقات، وقبول الفيديو والمكالمات باستخدامCall.Accept()
واجهة برمجة التطبيقات. - عند اتصال مكالمتك، تبدأ تلقائيًا في إرسال دفق فيديو من الكاميرا المحددة إلى المشارك الآخر.
بدء إرسال الفيديو المحلي وإيقافه أثناء إجراء مكالمة
بدء الفيديو
لبدء فيديو أثناء إجراء مكالمة، يجب تعداد الكاميرات باستخدام الأسلوب الموجود 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
مثيل مكالمة.
البحث عن دفق الفيديو في كائن الاتصال
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );
إيقاف الفيديو المحلي لإيقاف الفيديو المحلي أثناء إجراء مكالمة، مرر localVideoStream
المثيل المستخدم للفيديو إلى طريقة stopVideo ل Call
:
await call.stopVideo(localVideoStream);
يمكنك التبديل إلى جهاز كاميرا مختلف أثناء وجود LocalVideoStream نشط عن طريق استدعاء switchSource
على هذا LocalVideoStream
المثيل:
const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);
إذا لم يكن جهاز الفيديو المحدد متوفرا:
- أثناء إجراء مكالمة، إذا كان الفيديو الخاص بك متوقفا عن التشغيل وبدء تشغيل الفيديو باستخدام
call.startVideo()
، فإن هذا الأسلوب يطرحSourceUnavailableError
ويتم تعيين تشخيص مواجهةcameraStartFailed
المستخدم إلى صحيح. - يؤدي
localVideoStream.switchSource()
استدعاء الأسلوبcameraStartFailed
إلى تعيين إلى صحيح. يوفر دليل تشخيص المكالمات لدينا معلومات إضافية حول كيفية تشخيص المشكلات المتعلقة بالاتصال.
للتحقق مما إذا كان الفيديو المحلي قيد التشغيل أو إيقاف التشغيل، يمكنك استخدام Call
الأسلوب isLocalVideoStarted
، الذي يرجع صوابا أو خطأ:
// 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();
التحقق من حالة مشاركة الشاشة
للتحقق مما إذا كانت مشاركة الشاشة قيد التشغيل أو إيقاف التشغيل، يمكنك استخدام واجهة برمجة تطبيقاتScreenSharingOn، والتي ترجع صوابا أو خطأ:
// 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 الجدد والموجودين.
يتم توفير واجهات برمجة التطبيقات وSDKs للمعاينة دون اتفاقية على مستوى الخدمة. نوصي بعدم استخدامها لأحمال عمل الإنتاج. قد لا تكون بعض الميزات مدعومة أو قد تكون القدرات مقيدة.
لمزيد من المعلومات، راجع شروط الاستخدام التكميلية لمعاينات 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
. يمكنك بعد ذلك إرفاق 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 ميزة تسمى العدد الأمثل للفيديو (OVC)، بدءا من الإصدار 1.15.1.
استخدم هذه الميزة لإعلام التطبيقات في وقت التشغيل بعدد مقاطع الفيديو الواردة من مشاركين مختلفين التي يمكن عرضها على النحو الأمثل في لحظة معينة في مكالمة جماعية مكونة من اثنين (2) أو أكثر من المشاركين.
تعرض هذه الميزة خاصية optimalVideoCount
تتغير ديناميكيا أثناء المكالمة استنادا إلى إمكانات الشبكة والأجهزة لنقطة نهاية محلية. قيمة optimalVideoCount
تفاصيل عدد مقاطع الفيديو من تطبيق مشارك مختلف يجب تقديمها في لحظة معينة. يجب على التطبيقات التعامل مع هذه التغييرات وتحديث عدد مقاطع الفيديو المعروضة وفقا للتوصية. هناك فترة تصحيح (حوالي عشر (10) ثوان) بين كل تحديث.
الاستخدام
الميزة optimalVideoCount
هي ميزة استدعاء. تحتاج إلى الرجوع إلى الميزة OptimalVideoCount
عبر feature
أسلوب الكائن Call
.
يمكنك بعد ذلك تعيين وحدة استماع عبر on
أسلوب OptimalVideoCountCallFeature
ليتم إعلامك عند تغيير optimalVideoCount. لإلغاء الاشتراك في التغييرات، يمكنك استدعاء off
الأسلوب .
الحد الأقصى الحالي لعدد مقاطع الفيديو الواردة التي يمكن عرضها هو 16. لدعم 16 مقطع فيديو وارد بشكل صحيح، يحتاج الكمبيوتر إلى ذاكرة وصول عشوائي بسعة 16 غيغابايت كحد أدنى وأربعة (4) وحدة معالجة مركزية أساسية أو أكبر عمرها أقل من ثلاث (3) سنوات.
const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})
مثال على الاستخدام: يشترك تطبيقك في تغييرات "عدد الفيديو الأمثل" في مكالمات المجموعة. يتم التعامل مع التغيير في عدد الفيديو الأمثل إما عن طريق إنشاء أسلوب عارض createView
جديد أو التخلص من طرق العرض dispose
وتحديث تخطيط التطبيق وفقا لذلك.
خصائص دفق الفيديو عن بُعد
تحتوي تدفقات الفيديو عن بُعد على الخصائص التالية:
const id: number = remoteVideoStream.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 إلى خطأ. ليس عليك تنفيذ زيادة زيادة ونقصان التحميل، ولكن زيادة التحميل هي الاستخدام الأكثر شيوعا للحصول على تجربة مستخدم أفضل.
const size: StreamSize = remoteVideoStream.size;
size
: حجم الدفق مع معلومات حول عرض الفيديو وارتفاعه.
طرق وخصائص VideoStreamRenderer
await videoStreamRenderer.createView();
VideoStreamRendererView
إنشاء مثيل يمكن إرفاقه في واجهة مستخدم التطبيق لعرض دفق الفيديو البعيد، واستخدام أسلوب غير createView()
متزامن، ويحل عندما يكون الدفق جاهزا لعرض وإرجاع كائن بخاصية target
تمثل video
العنصر الذي يمكن إدراجه في أي مكان في شجرة DOM.
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');
إرسال تدفقات الفيديو من كاميرتين مختلفتين، في نفس المكالمة من نفس جهاز سطح المكتب.
هام
هذه الميزة من Azure Communication Services قيد المعاينة حاليا. الميزات في المعاينة متاحة للجمهور ويمكن استخدامها من قبل جميع عملاء Microsoft الجدد والموجودين.
يتم توفير واجهات برمجة التطبيقات وSDKs للمعاينة دون اتفاقية على مستوى الخدمة. نوصي بعدم استخدامها لأحمال عمل الإنتاج. قد لا تكون بعض الميزات مدعومة أو قد تكون القدرات مقيدة.
لمزيد من المعلومات، راجع شروط الاستخدام التكميلية لمعاينات Microsoft Azure.
يتم دعم إرسال تدفقات الفيديو من كاميرتين مختلفتين في نفس المكالمة كجزء من الإصدار 1.17.1-beta.1+ على المستعرضات المدعومة لسطح المكتب.
يمكنك إرسال تدفقات الفيديو من كاميرتين مختلفتين من علامة تبويب/تطبيق مستعرض سطح مكتب واحد، في نفس المكالمة، مع قصاصة التعليمات البرمجية التالية:
// 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();
القيود:
- يجب أن يتم إرسال تدفقات الفيديو مع مثيلين مختلفين
CallAgent
باستخدام هويات مختلفة. يظهر مقتطف التعليمات البرمجية اثنين من وكلاء الاستدعاءات قيد الاستخدام، لكل منهما كائن Call الخاص به. - في مثال التعليمات البرمجية، ينضم كلا CallAgents إلى نفس المكالمة (نفس معرفات المكالمة). يمكنك أيضا الانضمام إلى مكالمات مختلفة مع كل وكيل وإرسال فيديو واحد في مكالمة واحدة وفيديو مختلف على المكالمة الأخرى.
- إرسال نفس الكاميرا في كلا CallAgents غير مدعوم. يجب أن يكونا كاميرتين مختلفتين.
- إرسال كاميرتين مختلفتين مع 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
يمكنك الكائن من تعداد الأجهزة المحلية لاستخدامها في مكالمة لنقل تدفقات الصوت/الفيديو. كما يمكنك من طلب إذن من مستخدم للوصول إلى الميكروفون والكاميرا باستخدام واجهة برمجة تطبيقات المتصفح الأصلية.
للوصول إلى 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 باستخدام كاميرا الفيديو
تحذير
حاليا يتم دعم دفق فيديو محلي صادر واحد فقط. لوضع مكالمة مع الفيديو، يجب تعداد الكاميرات المحلية باستخدام deviceManager
getCameras
واجهة برمجة التطبيقات.
بمجرد تحديد كاميرا، استخدمها لإنشاء 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
في واجهة برمجة التطبيقات كوسيطة:
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
تغيير الخاصية إلى صحيح إلى أن المشارك البعيد يرسل حاليًّا دفقًا. بمجرد حدوث ذلك، قم بإنشاء مثيل جديد لـ Renderer
، ثم قم بإنشاء واجهة برمجة تطبيقات جديدةRendererView
باستخدام واجهة برمجة تطبيقات createView
غير متزامنة، وقم بإرفاقها في أي مكان في واجهة 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
- معرف دفق فيديو بعيد.int id = remoteVideoStream.getId();
MediaStreamType
- يمكن أن يكونVideo
أوScreenSharing
.MediaStreamType type = remoteVideoStream.getMediaStreamType();
isAvailable
- يشير إلى ما إذا كانت نقطة نهاية المشارك البعيد ترسل الدفق بنشاط.boolean availability = remoteVideoStream.isAvailable();
طرق وخصائص جهاز العرض
Renderer
يستخدم الكائن الأساليب التالية.
لعرض دفق الفيديو عن بعد، قم بإنشاء
VideoStreamRendererView
مثيل يمكن إرفاقه لاحقا في واجهة مستخدم التطبيق.// Create a view for a video stream VideoStreamRendererView.createView()
تخلص من جهاز العرض وكل ما يرتبط
VideoStreamRendererView
بجهاز العرض هذا. قم باستدعائه بعد إزالة كافة طرق العرض المقترنة من واجهة المستخدم.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 الذي تم إنشاؤه بواجهة مستخدم التطبيق باستخدام القصاصة البرمجية التالية:
layout.addView(rendererView);
يمكنك تحديث وضع التحجيم لاحقا باستخدام updateScalingMode
العملية على RendererView
الكائن باستخدام وسيطة إما ScalingMode.CROP
أو ScalingMode.FIT
.
// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)
قم بإعداد النظام
اتبع هذه الخطوات لإعداد النظام الخاص بك.
قم بإنشاء مشروع Xcode
في Xcode، أنشئ مشروع iOS جديداً ثم حدد قالب تطبيق العرض الفردي. تستخدم هذه المقالة إطار عمل SwiftUI، لذلك يجب عليك تعيين Language إلى Swift وتعيين Interface إلى SwiftUI.
لن تقوم بإنشاء اختبارات في هذه المقالة. لا تتردد في إلغاء تحديد خانة الاختيار تضمين الاختبارات .
تثبيت الحزمة والتبعيات باستخدام CocoaPods
إنشاء Podfile لتطبيقك، مثل هذا المثال:
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
شغّل
pod install
.افتح
.xcworkspace
باستخدام Xcode.
اطلب الوصول إلى الميكروفون
للوصول إلى ميكروفون الجهاز، تحتاج إلى تحديث قائمة خصائص معلومات التطبيق باستخدام NSMicrophoneUsageDescription
. قم بتعيين القيمة المقترنة إلى سلسلة مضمنة في مربع الحوار الذي يستخدمه النظام لطلب الوصول من المستخدم.
انقر بزر الماوس الأيمن فوق إدخال Info.plist لشجرة المشروع، ثم حدد Open As>Source Code. أضف الأسطر التالية في قسم المستوى <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، قم بإنشاء مشروع Blank App (Universal Windows) جديد. بعد إدخال اسم المشروع، لا تتردد في اختيار أي Windows SDK بعد 10.0.17763.0.
بالنسبة لتطبيق WinUI 3، قم بإنشاء مشروع جديد باستخدام قالب Blank App، Packaged (WinUI 3 in Desktop) لإعداد تطبيق WinUI 3 من صفحة واحدة. مطلوب SDK لتطبيق Windows الإصدار 1.3 أو أحدث.
تثبيت الحزمة والتبعيات باستخدام NuGet مدير الحِزَم
تتوفر واجهات برمجة تطبيقات SDK والمكتبات للاتصال بشكل عام عبر حزمة NuGet.
للبحث عن حزمة Calling SDK NuGet وتنزيلها وتثبيتها:
- افتح NuGet مدير الحِزَم عن طريق تحديد > إدارة حزم NuGet للحل.
- حدد استعراض، ثم أدخل Azure.Communication.Calling.WindowsClient في مربع البحث.
- تأكد من تحديد خانة الاختيار Include prerelease .
- حدد حزمة Azure.Communication.Calling.WindowsClient ، ثم حدد Azure.Communication.Calling.WindowsClient1.4.0-beta.1 أو إصدار أحدث.
- حدد خانة الاختيار التي تتوافق مع مشروع Azure Communication Services في الجزء الأيمن.
- حدد تثبيت.
اطلب الوصول إلى الميكروفون
يتطلب التطبيق الوصول إلى الكاميرا. في تطبيقات النظام الأساسي العام ل Windows (UWP)، تحتاج إلى الإعلان عن إمكانية الكاميرا في ملف بيان التطبيق.
- افتح المشروع في Visual Studio.
- في لوحة Solution Explorer ، انقر نقرا مزدوجا فوق الملف مع
.appxmanifest
الملحق. - انقر فوق علامة التبويب قدرات .
- حدد خانة
Camera
الاختيار من قائمة الإمكانات.
إنشاء أزرار واجهة المستخدم لوضع المكالمة وتعليقها
يحتوي هذا التطبيق النموذجي على زرين. واحد لوضع المكالمة، وآخر لتعليق مكالمة تم إجراؤها.
- في لوحة مستكشف الحلول ، انقر نقرا مزدوجا فوق الملف المسمى
MainPage.xaml
ل UWP، أوMainWindows.xaml
ل WinUI 3. - في اللوحة المركزية، ابحث عن رمز 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>
إعداد التطبيق باستخدام واجهات برمجة تطبيقات SDK
واجهات برمجة التطبيقات SDK الاستدعاء في اثنين من مساحات أسماء مختلفة.
أكمل الخطوات التالية لإعلام المحول البرمجي C# حول مساحات الأسماء هذه، ما يتيح Intellisense الخاص ب Visual Studio للمساعدة في تطوير التعليمات البرمجية.
- في لوحة Solution Explorer ، انقر فوق السهم الموجود على الجانب الأيمن من الملف المسمى
MainPage.xaml
ل UWP، أوMainWindows.xaml
ل WinUI 3. - انقر نقرا مزدوجا فوق الملف المسمى
MainPage.xaml.cs
أوMainWindows.xaml.cs
. - أضف الأوامر التالية في أسفل العبارات الحالية
using
.
using Azure.Communication.Calling.WindowsClient;
الاحتفاظ MainPage.xaml.cs
أو MainWindows.xaml.cs
الفتح. تضيف الخطوة التالية المزيد من التعليمات البرمجية.
تمكين تفاعلات التطبيق
تحتاج أزرار واجهة المستخدم التي أضفناها إلى العمل فوق وضع CommunicationCall
. وهذا يعني أنه يجب إضافة عضو CommunicationCall
بيانات إلى MainPage
الفئة أو MainWindow
.
تحتاج أيضا إلى تمكين العملية غير المتزامنة التي تم إنشاءها CallAgent
للنجاح. إضافة عضو CallAgent
بيانات إلى نفس الفئة.
أضف أعضاء البيانات التالية إلى MainPage
الفئة أو MainWindow
:
CallAgent callAgent;
CommunicationCall call;
إنشاء معالجات الأزرار
في السابق، أضفنا زرين لواجهة المستخدم إلى التعليمات البرمجية XAML. تضيف التعليمات البرمجية التالية المعالجات لتشغيلها عندما يحدد المستخدم الزر.
أضف التعليمات البرمجية التالية بعد أعضاء البيانات من القسم السابق.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// End the current call
}
نموذج الكائن
تعالج الفئات والواجهات التالية بعض الميزات الرئيسية من مكتبة عميل Azure Communication Services Calling لـ UWP.
الاسم | الوصف |
---|---|
CallClient |
CallClient هو نقطة الإدخال الرئيسية إلى مكتبة عميل الاتصال. |
CallAgent |
CallAgent يتم استخدام لبدء المكالمات والانضمام إليها. |
CommunicationCall |
CommunicationCall يتم استخدام لإدارة المكالمات الموضوعة أو المنضمة. |
CommunicationTokenCredential |
CommunicationTokenCredential يتم استخدام كبيانات اعتماد الرمز المميز لإنشاء مثيل .CallAgent |
CallAgentOptions |
CallAgentOptions يحتوي على معلومات لتعريف المتصل. |
HangupOptions |
يعلم HangupOptions ما إذا كان يجب إنهاء المكالمة لجميع المشاركين فيها. |
تسجيل معالج مخطط الفيديو
يتطلب مكون واجهة المستخدم، مثل XAML MediaElement
أو MediaPlayerElement
، من التطبيق تسجيل تكوين لعرض موجزات الفيديو المحلية والنائية.
أضف المحتوى التالي بين 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 لتقديم مكالمة إلى المستلم المحدد.
في المرة الأولى التي يتم فيها تشغيل التطبيق، يطالب النظام المستخدم بمنح حق الوصول إلى الميكروفون.