مشاركة عبر


إدارة الفيديو أثناء المكالمات

تعرف على كيفية إدارة مكالمات الفيديو باستخدام Azure Communication Services SDKS. سنتعلم كيفية إدارة تلقي الفيديو وإرساله في مكالمة.

المتطلبات الأساسية

تثبيت حزمة 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 الصفيف إلى CallAgentstartCall الأسلوب .

const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
const placeCallOptions = {videoOptions: {localVideoStreams:[localVideoStream]}};
const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const call = callAgent.startCall([userCallee], placeCallOptions);
  • يمكنك أيضا الانضمام إلى مكالمة باستخدام فيديو باستخدام 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 باستخدام كاميرا الفيديو

تحذير

حاليا يتم دعم دفق فيديو محلي صادر واحد فقط. لوضع مكالمة مع الفيديو، يجب تعداد الكاميرات المحلية باستخدام deviceManagergetCameras واجهة برمجة التطبيقات. بمجرد تحديد كاميرا، استخدمها لإنشاء 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);

ابدأ وتوقف عن إرسال الفيديو المحلي

لبدء فيديو، يجب تعداد الكاميرات باستخدام العملية على getCameraListdeviceManager الكائن. ثم أنشئ مثيلًا جديدًا لتمرير 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.

لن تقوم بإنشاء اختبارات في هذه المقالة. لا تتردد في إلغاء تحديد خانة الاختيار تضمين الاختبارات .

لقطة شاشة تعرض نافذة إنشاء مشروع داخل Xcode.

تثبيت الحزمة والتبعيات باستخدام CocoaPods

  1. إنشاء Podfile لتطبيقك، مثل هذا المثال:

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

  3. افتح .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 وتنزيلها وتثبيتها:

  1. افتح NuGet مدير الحِزَم عن طريق تحديد > إدارة حزم NuGet للحل.
  2. حدد استعراض، ثم أدخل Azure.Communication.Calling.WindowsClient في مربع البحث.
  3. تأكد من تحديد خانة الاختيار Include prerelease .
  4. حدد حزمة Azure.Communication.Calling.WindowsClient ، ثم حدد Azure.Communication.Calling.WindowsClient1.4.0-beta.1 أو إصدار أحدث.
  5. حدد خانة الاختيار التي تتوافق مع مشروع Azure Communication Services في الجزء الأيمن.
  6. حدد تثبيت.

اطلب الوصول إلى الميكروفون

يتطلب التطبيق الوصول إلى الكاميرا. في تطبيقات النظام الأساسي العام ل Windows (UWP)، تحتاج إلى الإعلان عن إمكانية الكاميرا في ملف بيان التطبيق.

  1. افتح المشروع في Visual Studio.
  2. في لوحة Solution Explorer ، انقر نقرا مزدوجا فوق الملف مع .appxmanifest الملحق.
  3. انقر فوق علامة التبويب قدرات .
  4. حدد خانة Camera الاختيار من قائمة الإمكانات.

إنشاء أزرار واجهة المستخدم لوضع المكالمة وتعليقها

يحتوي هذا التطبيق النموذجي على زرين. واحد لوضع المكالمة، وآخر لتعليق مكالمة تم إجراؤها.

  1. في لوحة مستكشف الحلول ، انقر نقرا مزدوجا فوق الملف المسمى MainPage.xaml ل UWP، أو MainWindows.xaml ل WinUI 3.
  2. في اللوحة المركزية، ابحث عن رمز XAML ضمن معاينة واجهة المستخدم.
  3. تعديل التعليمات البرمجية 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 للمساعدة في تطوير التعليمات البرمجية.

  1. في لوحة Solution Explorer ، انقر فوق السهم الموجود على الجانب الأيمن من الملف المسمى MainPage.xaml ل UWP، أو MainWindows.xaml ل WinUI 3.
  2. انقر نقرا مزدوجا فوق الملف المسمى MainPage.xaml.cs أو MainWindows.xaml.cs.
  3. أضف الأوامر التالية في أسفل العبارات الحالية 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;
            }
        }
    }
}

تشغيل التعليمات البرمجية

  1. تأكد من أن Visual Studio ينشئ التطبيق ل x64أو x86أو ARM64.
  2. اضغط على F5 لبدء تشغيل التطبيق.
  3. انقر فوق الزر CommunicationCall لتقديم مكالمة إلى المستلم المحدد.

في المرة الأولى التي يتم فيها تشغيل التطبيق، يطالب النظام المستخدم بمنح حق الوصول إلى الميكروفون.

الخطوات التالية