بدء استخدام نموذج بطل الاستدعاء

Azure Communication ServicesGroup Calling Hero Sample يوضح كيف يمكن استخدام Communication Services Calling Web SDK لإنشاء تجربة مكالمة مجموعة.

في هذا التشغيل السريع للعينة، نتعلم كيفية عمل العينة قبل تشغيل العينة على جهازك المحلي ثم نشر النموذج إلى Azure باستخدام موارد Azure Communication Services الخاصة بك.

تنزيل التعليمات البرمجية

ابحث عن المشروع لهذا النموذج على GitHub. يمكن العثور على إصدار من العينة يتضمن ميزات حاليا في المعاينة العامة مثل Teams Interop وتسجيل المكالمات في فرع منفصل.

نشر في Azure

نظرة عامة

يحتوي النموذج على تطبيق من جانب العميل وتطبيق من جانب الخادم. تطبيق من جانب العميل هو تطبيق ويب React/Redux يستخدم واجهة المستخدم Microsoft's Fluent. يرسل هذا التطبيق طلبات إلى تطبيق من جانب الخادم ASP.NET Core والذي يساعد التطبيق من جانب العميل على الاتصال بـ Azure.

وهكذا يبدو النموذج:

لقطة شاشة تعرض الصفحة المقصودة من نموذج التطبيق.

عند الضغط على الزر"بدء مكالمة"، يجلب تطبيق الويب الرمز المميز لوصول المستخدم من التطبيق من جانب الخادم. ثم يتم استخدام هذا الرمز المميز لتوصيل العميل إلى Azure Communication Services. بمجرد استرداد الرمز المميز، تتم مطالبتك بتحديد الكاميرا والميكروفون الذي تريد استخدامه. يمكنك تعطيل/تمكين أجهزتك باستخدام عناصر تحكم التبديل:

لقطة شاشة تعرض شاشة ما قبل المكالمة من نموذج التطبيق.

بمجرد تكوين اسم العرض والأجهزة، يمكنك الانضمام إلى مدة المكالمة. سترى لوحة الاتصال الرئيسية حيث تعيش تجربة الاتصال الأساسية.

لقطة شاشة تعرض الشاشة الأساسية لنموذج التطبيق.

مكونات شاشة المكالمة الرئيسية:

  • معرض الوسائط: المرحلة الرئيسية التي يظهر فيها المشاركون. إذا تم تمكين الكاميرا الخاصة بالمشارك، يتم عرض ملف الفيديو الخاص به هنا. لكل مشارك لوحة فردية تعرض اسم العرض وبث الفيديو (عندما يكون هناك واحد)
  • العنوان: هذا هو المكان الذي توجد به عناصر التحكم في المكالمة الأساسية للتبديل بين الإعدادات وشريط جانب المشاركين، وتشغيل الفيديو ومزج بين تشغيل / إيقاف، ومشاركة الشاشة والانسحاب من المكالمة.
  • الشريط الجانبي:هذا هو المكان الذي يتم فيه عرض معلومات المشاركين والإعدادات عند التبديل باستخدام عناصر التحكم الموجودة في العنوان. يمكن رفض المكون باستخدام "X" الموجود في الزاوية اليمنى العليا. يعرض الشريط الجانبي للمشاركين قائمة بالمشاركين وارتباطا لدعوة المزيد من المستخدمين للدردشة. يسمح شريط الإعدادات الجانبي لك بتكوين إعدادات الميكروفون والكاميرا.

أدناه يمكنك العثور على مزيد من المعلومات حول المتطلبات الأساسية والخطوات لإعداد العينة.

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

قبل تشغيل العينة لأول مرة

  1. افتح مثيل PowerShell أو Windows Terminal أو Command Prompt أو ما يعادل ذلك، وانتقل إلى الدليل الذي تريد استنساخ النموذج إليه.

    git clone https://github.com/Azure-Samples/communication-services-web-calling-hero.git
    
  2. احصل على Connection String من مدخل Microsoft Azure أو باستخدام Azure CLI.

    az communication list-key --name "<acsResourceName>" --resource-group "<resourceGroup>"
    

    لمزيد من المعلومات حول سلسلة الاتصال، راجع إنشاء موارد اتصالات Azure

  3. بمجرد الحصول على Connection String، أضف سلسلة الاتصال إلى ملف samples/Server/appsetting.json. إدخال سلسلة الاتصال في المتغير: ResourceConnectionString.

  4. احصل على Endpoint string من مدخل Microsoft Azure أو باستخدام Azure CLI.

    az communication list-key --name "<acsResourceName>" --resource-group "<resourceGroup>"
    

    لمزيد من المعلومات حول سلاسل نقطة النهاية، راجع إنشاء موارد اتصالات Azure

  5. بمجرد الحصول على Endpoint String، أضف سلسلة نقطة النهاية إلى ملف samples/Server/appsetting.json . إدخال سلسلة نقطة النهاية في المتغير EndpointUrl

تشغيل محلي

  1. تثبيت التبعيات

    npm run setup
    
  2. بدء تشغيل تطبيق الاتصال

    npm run start
    

    يؤدي ذلك إلى فتح خادم عميل على المنفذ 3000 الذي يخدم ملفات موقع الويب، وخادم واجهة برمجة التطبيقات على المنفذ 8080 الذي يؤدي وظائف مثل الرموز المميزة للتصغير للمشاركين في المكالمات.

استكشاف الأخطاء وإصلاحها

  • يعرض التطبيق شاشة "مستعرض غير مدعوم" لكنني على مستعرض مدعوم.

    إذا تم تقديم تطبيقك عبر اسم مضيف آخر غير localhost، فيجب عليك خدمة نسبة استخدام الشبكة عبر https وليس http.

نشر إلى Azure

  1. npm run setup
  2. npm run build
  3. npm run package
  4. استخدم ملحق Azure وانشر دليل Calling/dist في خدمة التطبيق

تنظيف الموارد

إذا كنت ترغب في تنظيف وإزالة اشتراك Communication Services، يمكنك حذف المورد أو مجموعة الموارد. يؤدي حذف مجموعة الموارد إلى حذف أية موارد أخرى مقترنة بها أيضًا. تعرّف على المزيد حول تنظيف الموارد.

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

لمزيد من المعلومات، راجع المقالات التالية:

لقراءة إضافية

  • النماذج - تعرف على المزيد من النماذج والأمثلة على صفحة نظرة عامة على النماذج.
  • Redux - إدارة الحالة من جانب العميل
  • FluentUI - مكتبة واجهة مستخدم مدعومة من Microsoft
  • React - مكتبة لبناء واجهات المستخدم
  • ASP.NET Core - إطار عمل لبناء تطبيقات الويب

يوضح نموذج Azure Communication Services Group Calling Hero لنظام التشغيل iOS كيفية استخدام Communication Services Calling iOS SDK لإنشاء تجربة مكالمات جماعية تتضمن الصوت والفيديو. في هذه البداية السريعة عينة، سوف تتعلم كيفية إعداد وتشغيل العينة. يتم توفير نظرة عامة على العينة للسياق.

تنزيل التعليمات البرمجية

ابحث عن المشروع لهذا النموذج على GitHub.

نظرة عامة

النموذج هو تطبيق iOS أصلي يستخدم Azure Communication Services iOS SDKs لإنشاء تجربة مكالمة تتميز بمكالمة صوتية وفيديو. يستخدم التطبيق مكونًا من جانب الملقم لتوفير رموز الوصول المميزة التي يتم استخدامها بعد ذلك لبدء SDK خدمات الاتصالات Azure. لتكوين هذا المكون من جانب الخادم، لا تتردد في اتباع البرنامج التعليمي Trusted Service with Azure Functions .

وهكذا يبدو النموذج:

لقطة شاشة تعرض الصفحة المقصودة من نموذج التطبيق.

عند الضغط على الزر "بدء مكالمة جديدة"، يطالبك تطبيق iOS بإدخال اسم العرض لاستخدامه في المكالمة.

لقطة شاشة تعرض شاشة ما قبل المكالمة من نموذج التطبيق.

بعد الضغط على "التالي" على شاشة "بدء المكالمة"، ستتوفر لك الفرصة لمشاركة معرف المجموعة للمكالمة عبر ورقة مشاركة iOS.

لقطة شاشة تعرض شاشة معرف مجموعة المشاركة من نموذج التطبيق.

يسمح لك التطبيق أيضا بالانضمام إلى مكالمة Azure Communication Services موجودة عن طريق تحديد معرف المكالمة الحالية أو ارتباط معرف الفرق.

لقطة شاشة تعرض شاشة مكالمة الانضمام لنموذج التطبيق.

بعد الانضمام إلى مكالمة، ستتم مطالبتك بمنح التطبيق الإذن للوصول إلى الكاميرا والميكروفون، إن لم يكن مصرحا به بالفعل. ضع في اعتبارك أن وظائف الصوت والفيديو الحقيقية متوفرة فقط على الأجهزة الحقيقية، مثل جميع التطبيقات المستندة إلى AVFoundation.

بمجرد تكوين اسم العرض والانضمام إلى المكالمة، سترى لوحة الاتصال الرئيسية حيث توجد تجربة الاتصال الأساسية.

لقطة شاشة تعرض الشاشة الأساسية لنموذج التطبيق.

مكونات شاشة المكالمة الرئيسية:

  • معرض الوسائط: المرحلة الرئيسية التي يظهر فيها المشاركون. إذا تم تمكين الكاميرا الخاصة بالمشارك، يتم عرض ملف الفيديو الخاص به هنا. يحتوي كل مشارك على تجانب منفرد يظهر اسم العرض وتدفق الفيديو (عندما يكون هناك واحد). يدعم المعرض مشاركين متعددين ويتم تحديثه عند إضافة المشاركين أو إزالتهم إلى المكالمة.
  • شريط الإجراءات: هذا هو المكان الذي توجد فيه عناصر التحكم في المكالمات الأساسية. تتيح لك عناصر التحكم هذه تشغيل/إيقاف تشغيل الفيديو والميكروفون ومشاركة شاشتك وترك المكالمة.

وستجد أدناه المزيد من المعلومات حول المتطلبات الأساسية والخطوات لإعداد النموذج.

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

  • حساب Azure مع اشتراك نشط. لمزيد من التفاصيل، راجع إنشاء حساب مجانًا.
  • جهاز Mac يعمل بنظام تشغيل Xcode، بالإضافة إلى شهادة مطور صالحة مثبتة على Keychain لديك.
  • مورد Azure Communication Services. لمعرفة التفاصيل، انظر إنشاء مورد Azure Communication Services.
  • تعمل وظيفة Azure على تشغيل نقطة نهاية المصادقة لجلب رموز الوصول المميزة.

تشغيل نموذج محليًا

يمكن تشغيل نموذج مكالمة المجموعة محليًا باستخدام XCode. يمكن للمطورين إما استخدام الجهاز الفعلي أو المحاكي لاختبار التطبيق.

قبل تشغيل العينة لأول مرة

  1. تثبيت التبعيات عن طريق تشغيل pod install.
  2. افتح AzureCalling.xcworkspace في XCode.
  3. إنشاء ملف نصي في الجذر، يسمى AppSettings.xcconfig وتعيين القيمة:
    communicationTokenFetchUrl = <your authentication endpoint, without the https:// component>
    

قم بتشغيل النموذج

قم بإنشاء وتشغيل العينة في XCode، باستخدام هدف AzureCalling على جهاز المحاكاة أو الجهاز الذي تختاره.

(اختياري) تأمين نقطة نهاية مصادقة

لأغراض العرض التوضيحي، يستخدم هذا النموذج نقطة نهاية يمكن الوصول إليها بشكل عام بشكل افتراضي لجلب رمز وصول Azure Communication Services. بالنسبة إلى سيناريوهات الإنتاج، نوصي باستخدام نقطة النهاية الآمنة الخاصة بك لتوفير الرموز المميزة الخاصة بك.

مع تكوين إضافي، يدعم هذا النموذج الاتصال بنقطة نهاية محمية لمعرف Microsoft Entra (معرف Microsoft Entra) بحيث يكون تسجيل دخول المستخدم مطلوبا للتطبيق لجلب رمز مميز للوصول إلى Azure Communication Services. انظر الخطوات أدناه:

  1. تمكين مصادقة Microsoft Entra في تطبيقك.
  2. انتقل إلى صفحة نظرة عامة على التطبيق المسجل ضمن Microsoft Entra App Registrations. دون ملاحظة عن Application (client) ID، Directory (tenant) IDApplication ID URI

تكوين Microsoft Entra على مدخل Microsoft Azure.

  1. أنشئ ملفا AppSettings.xcconfig في الجذر إذا لم يكن موجودا بالفعل وأضف القيم:
    communicationTokenFetchUrl = <Application ID URI, without the https:// component>
    aadClientId = <Application (client) ID>
    aadTenantId = <Directory (tenant) ID>
    

تنظيف الموارد

إذا كنت ترغب في تنظيف وإزالة اشتراك Communication Services، يمكنك حذف المورد أو مجموعة الموارد. يؤدي حذف مجموعة الموارد إلى حذف أية موارد أخرى مقترنة بها أيضًا. تعرّف على المزيد حول تنظيف الموارد.

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

لمزيد من المعلومات، راجع المقالات التالية:

لقراءة إضافية

يوضح نموذج Azure Communication Services Group Calling Hero لنظام Android كيف يمكن استخدام Communication Services Calling Android SDK لإنشاء تجربة اتصال جماعية تتضمن الصوت والفيديو. في هذه البداية السريعة عينة، سوف تتعلم كيفية إعداد وتشغيل العينة. يتم توفير نظرة عامة على العينة للسياق.

تنزيل التعليمات البرمجية

ابحث عن المشروع لهذا النموذج على GitHub.

نظرة عامة

العينة هي تطبيق Android أصلي يستخدم مكتبة عميل واجهة مستخدم Azure Communication Services Android لإنشاء تجربة اتصال تتميز بكل من مكالمات الصوت والفيديو. يستخدم التطبيق مكونًا من جانب الملقم لتوفير رموز الوصول المميزة التي يتم استخدامها بعد ذلك لبدء SDK خدمات الاتصالات Azure. لتكوين هذا المكون من جانب الخادم، لا تتردد في اتباع البرنامج التعليمي Trusted Service with Azure Functions .

وهكذا يبدو النموذج:

لقطة شاشة تعرض الصفحة المقصودة من نموذج التطبيق.

عند الضغط على الزر "بدء مكالمة جديدة"، يطالبك تطبيق Android بإدخال اسم العرض لاستخدامه في المكالمة.

لقطة شاشة تعرض شاشة ما قبل المكالمة من نموذج التطبيق.

بعد الضغط على "التالي" في صفحة "بدء مكالمة"، ستتوفر لديك الفرصة لمشاركة "معرف مكالمة المجموعة".

لقطة شاشة تعرض شاشة مشاركة معرف مكالمة المجموعة من نموذج التطبيق.

يسمح لك التطبيق بالانضمام إلى مكالمة Azure Communication Services موجودة عن طريق تحديد معرف المكالمة الحالية أو ارتباط معرف اجتماع الفرق واسم العرض.

لقطة شاشة تعرض شاشة مكالمة الانضمام لنموذج التطبيق.

بعد الانضمام إلى مكالمة، ستتم مطالبتك بمنح التطبيق الإذن للوصول إلى الكاميرا والميكروفون، إن لم يكن مصرحا به بالفعل. سترى لوحة الاتصال الرئيسية حيث تعيش تجربة الاتصال.

لقطة شاشة تعرض الشاشة الأساسية لنموذج التطبيق.

مكونات شاشة المكالمة الرئيسية:

  • معرض الوسائط: المرحلة الرئيسية التي يظهر فيها المشاركون. إذا تم تمكين الكاميرا الخاصة بالمشارك، يتم عرض ملف الفيديو الخاص به هنا. يحتوي كل مشارك على تجانب منفرد يظهر اسم العرض وتدفق الفيديو (عندما يكون هناك واحد). يدعم المعرض مشاركين متعددين ويتم تحديثه عند إضافة المشاركين أو إزالتهم إلى المكالمة.
  • شريط الإجراءات: هذا هو المكان الذي توجد فيه عناصر التحكم في المكالمات الأساسية. تتيح لك عناصر التحكم هذه تشغيل/إيقاف تشغيل الفيديو والميكروفون ومشاركة شاشتك وترك المكالمة.

وستجد أدناه المزيد من المعلومات حول المتطلبات الأساسية والخطوات لإعداد النموذج.

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

تشغيل نموذج محليًا

يمكن تشغيل عينة استدعاء المجموعة محليًا باستخدام Android Studio. يمكن للمطورين إما استخدام الجهاز الفعلي أو المحاكي لاختبار التطبيق.

قبل تشغيل العينة لأول مرة

  1. افتح Android Studio وحدد Open an Existing Project
  2. افتح AzureCalling المجلد داخل الإصدار الذي تم تنزيله للعينة.
  3. قم بتوسيع التطبيق/الأصول لتحديث appSettings.properties. قم بتعيين قيمة المفتاح communicationTokenFetchUrl ليكون عنوان URL لنقطة نهاية المصادقة التي تم إعدادها كشرط أساسي.

قم بتشغيل النموذج

قم ببناء وتشغيل العينة في Android Studio.

(اختياري) تأمين نقطة نهاية مصادقة

لأغراض العرض التوضيحي، يستخدم هذا النموذج نقطة نهاية يمكن الوصول إليها بشكل عام بشكل افتراضي لجلب رمز لـ Azure Communication Services. بالنسبة إلى سيناريوهات الإنتاج، نوصي باستخدام نقطة النهاية الآمنة الخاصة بك لتوفير الرموز المميزة الخاصة بك.

مع تكوين إضافي، يدعم هذا النموذج الاتصال بنقطة نهاية محمية لمعرف Microsoft Entra (معرف Microsoft Entra) بحيث يكون تسجيل دخول المستخدم مطلوبا للتطبيق لجلب رمز Azure Communication Services المميز. انظر الخطوات أدناه:

  1. تمكين مصادقة Microsoft Entra في تطبيقك.

  2. انتقل إلى صفحة نظرة عامة على التطبيق المسجل ضمن Microsoft Entra App Registrations. دون ، Package name، Signature hashMSAL Configutaion.

تكوين Microsoft Entra على مدخل Microsoft Azure.

  1. تحرير AzureCalling/app/src/main/res/raw/auth_config_single_account.json وتعيين isAADAuthEnabled لتمكين معرف Microsoft Entra.

  2. تحرير AndroidManifest.xml وتعيين android:path إلى تجزئة توقيع مخزن المفاتيح. (اختياري. تستخدم القيمة الحالية التجزئة من debug.keystore المجمع. إذا تم استخدام مخزن مفاتيح مختلف، يجب تحديث هذا.)

    <activity android:name="com.microsoft.identity.client.BrowserTabActivity">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <data
                     android:host="com.azure.samples.communication.calling"
                     android:path="/Signature hash" <!-- do not remove /. The current hash in AndroidManifest.xml is for debug.keystore. -->
                     android:scheme="msauth" />
             </intent-filter>
         </activity>
    
  3. انسخ تكوين MSAL Android من مدخل Microsoft Azure والصق إلى AzureCalling/app/src/main/res/raw/auth_config_single_account.json. تضمين "account_mode": "SINGLE"

       {
          "client_id": "",
          "authorization_user_agent": "DEFAULT",
          "redirect_uri": "",
          "account_mode" : "SINGLE",
          "authorities": [
             {
                "type": "AAD",
                "audience": {
                "type": "AzureADMyOrg",
                "tenant_id": ""
                }
             }
          ]
       }
    
  4. قم بتحرير AzureCalling/app/src/main/res/raw/auth_config_single_account.json وتعيين قيمة المفتاح communicationTokenFetchUrl ليكون عنوان URL لنقطة نهاية المصادقة الآمنة.

  5. تحرير AzureCalling/app/src/main/res/raw/auth_config_single_account.json وتعيين قيمة المفتاح aadScopes من Azure Active DirectoryExpose an API النطاقات

  6. تعيين قيمة ل graphURL في AzureCalling/app/assets/appSettings.properties كنقطة نهاية Graph API لجلب معلومات المستخدم.

  7. قم بتحرير AzureCalling/app/src/main/assets/appSettings.properties وتعيين قيمة المفتاح tenant لتمكين تسجيل الدخول الصامت بحيث لا يتعين مصادقة المستخدم مرارا وتكرارا أثناء إعادة تشغيل التطبيق.

تنظيف الموارد

إذا كنت ترغب في تنظيف وإزالة اشتراك Communication Services، يمكنك حذف المورد أو مجموعة الموارد. يؤدي حذف مجموعة الموارد إلى حذف أية موارد أخرى مقترنة بها أيضًا. تعرّف على المزيد حول تنظيف الموارد.

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

لمزيد من المعلومات، راجع المقالات التالية:

لقراءة إضافية

يوضح نموذج Azure Communication Services Group Calling Hero لنظام التشغيل Windows كيف يمكن استخدام Communication Services Calling Windows SDK لإنشاء تجربة مكالمات جماعية تتضمن الصوت والفيديو. في هذه العينة، ستتعلم كيفية إعداد العينة وتشغيلها. يتم توفير نظرة عامة على العينة للسياق.

في هذا التشغيل السريع، ستتعلم كيفية بدء مكالمة فيديو 1:1 باستخدام Azure Communication Services Calling SDK لنظام التشغيل Windows.

نموذج التعليمات البرمجية للنظام UWP

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

لإكمال هذا البرنامج التعليمي، تحتاج إلى المتطلبات الأساسية التالية:

الإعداد

إنشاء المشروع

في Visual Studio، قم بإنشاء مشروع جديد باستخدام قالب تطبيق فارغ (Windows عام) لإعداد تطبيق النظام الأساسي العام لـ Windows من صفحة واحدة.

لقطة شاشة تعرض نافذة New UWP Project داخل Visual Studio.

تثبيت الحزمة

انقر بزر الماوس الأيمن فوق المشروع وانتقل إلى Manage Nuget Packages لتثبيت Azure.Communication.Calling.WindowsClientالإصدار 1.2.0-beta.1 أو إصدار فائق. تأكد من تحديد Include Preleased.

طلب الإذن بالوصول

انتقل إلى Package.appxmanifest وانقر فوق Capabilities. تحقق من Internet (Client & Server) للحصول على وصول إلى الإنترنت صادر ووارد. تحقق من Microphone للوصول إلى بيانات الصوت الواردة من الميكروفون. تحقق من WebCam للوصول إلى كاميرا الجهاز.

أضف التعليمات البرمجية التالية إلى الزر الأيمن بالنقر فوق رمز Package.appxmanifest العرض واختياره.

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

إعداد إطار عمل التطبيق

نحن بحاجة إلى تكوين تخطيط أساسي لإرفاق منطقنا. من أجل إجراء مكالمة صادرة، نحتاج إلى TextBox توفير معرف المستخدم للمتصل. نحتاج أيضًا إلى زر Start Call وزر Hang Up. نحتاج أيضًا إلى معاينة الفيديو المحلي وعرض الفيديو عن بُعد للمشارك الآخر. لذلك نحن بحاجة إلى عنصرين لعرض تيارات الفيديو.

افتح MainPage.xaml، واستبدل المحتوى بالتنفيذ التالي:

<Page
    x:Class="CallingQuickstart.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CallingQuickstart"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid x:Name="MainGrid" HorizontalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="200*"/>
            <RowDefinition Height="60*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" x:Name="AppTitleBar" Background="LightSeaGreen">
            <!-- Width of the padding columns is set in LayoutMetricsChanged handler. -->
            <!-- Using padding columns instead of Margin ensures that the background paints the area under the caption control buttons (for transparent buttons). -->
            <TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="4,4,0,0"/>
        </Grid>

        <TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" />

        <Grid Grid.Row="2" Background="LightGray">
            <Grid.RowDefinitions>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
            <MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
        </Grid>
        <StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
            <StackPanel Orientation="Horizontal" Margin="10">
                <TextBlock VerticalAlignment="Center">Cameras:</TextBlock>
                <ComboBox x:Name="CameraList" HorizontalAlignment="Left" Grid.Column="0" DisplayMemberPath="Name" SelectionChanged="CameraList_SelectionChanged" Margin="10"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
                <Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
                <CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
                <CheckBox x:Name="BackgroundBlur" Content="Background blur" Width="142" Margin="10,0,0,0" Click="BackgroundBlur_Click"/>
            </StackPanel>
        </StackPanel>
        <TextBox Grid.Row="4" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
    </Grid>
</Page>

افتح App.xaml.cs (انقر بزر الماوس الأيمن واختر عرض التعليمات البرمجية) وأضف هذا السطر إلى الأعلى:

using CallingQuickstart;

افتح MainPage.xaml.cs(انقر بزر الماوس الأيمن واختر عرض الرمز) واستبدل المحتوى بالتنفيذ التالي:

using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Media.Core;
using Windows.Networking.PushNotifications;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace CallingQuickstart
{
    public sealed partial class MainPage : Page
    {
        private const string authToken = "<Azure Communication Services auth token>";
    
        private CallClient callClient;
        private CallTokenRefreshOptions callTokenRefreshOptions;
        private CallAgent callAgent;
        private CommunicationCall call = null;

        private LocalOutgoingAudioStream micStream;
        private LocalOutgoingVideoStream cameraStream;

        #region Page initialization
        public MainPage()
        {
            this.InitializeComponent();
            
            // Hide default title bar.
            var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
            coreTitleBar.ExtendViewIntoTitleBar = true;

            QuickstartTitle.Text = $"{Package.Current.DisplayName} - Ready";
            Window.Current.SetTitleBar(AppTitleBar);

            CallButton.IsEnabled = true;
            HangupButton.IsEnabled = !CallButton.IsEnabled;
            MuteLocal.IsChecked = MuteLocal.IsEnabled = !CallButton.IsEnabled;

            ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(800, 600);
            ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
        }

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            await InitCallAgentAndDeviceManagerAsync();

            base.OnNavigatedTo(e);
        }
#endregion

        private async Task InitCallAgentAndDeviceManagerAsync()
        {
            // Initialize call agent and Device Manager
        }

        private async void Agent_OnIncomingCallAsync(object sender, IncomingCall incomingCall)
        {
            // Accept an incoming call
        }

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

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

        private async void Call_OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
        {
            var call = sender as CommunicationCall;

            if (call != null)
            {
                var state = call.State;

                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    QuickstartTitle.Text = $"{Package.Current.DisplayName} - {state.ToString()}";
                    Window.Current.SetTitleBar(AppTitleBar);

                    HangupButton.IsEnabled = state == CallState.Connected || state == CallState.Ringing;
                    CallButton.IsEnabled = !HangupButton.IsEnabled;
                    MuteLocal.IsEnabled = !CallButton.IsEnabled;
                });

                switch (state)
                {
                    case CallState.Connected:
                        {
                            break;
                        }
                    case CallState.Disconnected:
                        {
                            break;
                        }
                    default: break;
                }
            }
        }
        
        private async void CameraList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Handle camera selection
        }
    }
}

نموذج الكائن

تتعامل الفئات والواجهات التالية مع بعض الميزات الرئيسية لـAzure Communication Services Calling SDK:

Name ‏‏الوصف
CallClient CallClient هو نقطة الإدخال الرئيسية إلى مكتبة عميل الاتصال.
CallAgent CallAgent يتم استخدام لبدء المكالمات والانضمام إليها.
CommunicationCall CommunicationCall يتم استخدام لإدارة المكالمات الموضوعة أو المنضمة.
CallTokenCredential CallTokenCredential يتم استخدام كبيانات اعتماد الرمز المميز لإنشاء مثيل .CallAgent
CommunicationUserIdentifier CommunicationUserIdentifier يتم استخدام لتمثيل هوية المستخدم، والتي يمكن أن تكون أحد الخيارات التالية: CommunicationUserIdentifierأو PhoneNumberIdentifier .CallingApplication

مصادقة العميل

لتهيئة CallAgent، تحتاج إلى رمز مميز لوصول المستخدم. بشكل عام يتم إنشاء هذا الرمز المميز من خدمة مع مصادقة خاصة بالتطبيق. لمزيد من المعلومات حول الرموز المميزة لوصول المستخدم، تحقق من دليل الرموز المميزة لوصول المستخدم.

لبدء التشغيل السريع، استبدل <AUTHENTICATION_TOKEN> برمز الوصول المميز لمستخدم تم إنشاؤه لمورد Azure Communication Service.

بمجرد أن يكون لديك رمز مميز، قم بتهيئة CallAgent مثيل معه، ما يمكننا من إجراء المكالمات وتلقيها. من أجل الوصول إلى الكاميرات على الجهاز، نحتاج أيضا إلى الحصول على مثيل إدارة الأجهزة.

أضف الرمز التالي إلى دالة InitCallAgentAndDeviceManagerAsync.

this.callClient = new CallClient(new CallClientOptions() {
    Diagnostics = new CallDiagnosticsOptions() { 
        AppName = "CallingQuickstart",
        AppVersion="1.0",
        Tags = new[] { "Calling", "ACS", "Windows" }
        }
    });

// Set up local video stream using the first camera enumerated
var deviceManager = await this.callClient.GetDeviceManagerAsync();
var camera = deviceManager?.Cameras?.FirstOrDefault();
var mic = deviceManager?.Microphones?.FirstOrDefault();
micStream = new LocalOutgoingAudioStream();

CameraList.ItemsSource = deviceManager.Cameras.ToList();

if (camera != null)
{
    CameraList.SelectedIndex = 0;
}

callTokenRefreshOptions = new CallTokenRefreshOptions(false);
callTokenRefreshOptions.TokenRefreshRequested += OnTokenRefreshRequestedAsync;

var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);

var callAgentOptions = new CallAgentOptions()
{
    DisplayName = "Contoso",
    //https://github.com/lukes/ISO-3166-Countries-with-Regional-Codes/blob/master/all/all.csv
    EmergencyCallOptions = new EmergencyCallOptions() { CountryCode = "840" }
};


try
{
    this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
    //await this.callAgent.RegisterForPushNotificationAsync(await this.RegisterWNS());
    this.callAgent.CallsUpdated += OnCallsUpdatedAsync;
    this.callAgent.IncomingCallReceived += OnIncomingCallAsync;

}
catch(Exception ex)
{
    if (ex.HResult == -2147024809)
    {
        // E_INVALIDARG
        // Handle possible invalid token
    }
}

بدء مكالمة باستخدام الفيديو

أضف التنفيذ إلى CallButton_Click لبدء مكالمة باستخدام الفيديو. نحن بحاجة إلى تعداد الكاميرات مع مثيل مدير الجهاز وبناء LocalOutgoingVideoStream . لبدء مكالمة بالفيديو، نحتاج إلى ضبط VideoOptions مع LocalVideoStream وتمريرها مع startCallOptions لتعيين الخيارات الأولية للمكالمة. من خلال إرفاق LocalOutgoingVideoStream ب MediaElement، يمكننا رؤية معاينة الفيديو المحلي.

var callString = CalleeTextBox.Text.Trim();

if (!string.IsNullOrEmpty(callString))
{
    if (callString.StartsWith("8:")) // 1:1 Azure Communication Services call
    {
        call = await StartAcsCallAsync(callString);
    }
    else if (callString.StartsWith("+")) // 1:1 phone call
    {
        call = await StartPhoneCallAsync(callString, "+12133947338");
    }
    else if (Guid.TryParse(callString, out Guid groupId))// Join group call by group guid
    {
        call = await JoinGroupCallByIdAsync(groupId);
    }
    else if (Uri.TryCreate(callString, UriKind.Absolute, out Uri teamsMeetinglink)) //Teams meeting link
    {
        call = await JoinTeamsMeetingByLinkAsync(teamsMeetinglink);
    }
}

if (call != null)
{
    call.RemoteParticipantsUpdated += OnRemoteParticipantsUpdatedAsync;
    call.StateChanged += OnStateChangedAsync;
}

أضف الأساليب لبدء أو الانضمام إلى أنواع مختلفة من المكالمات (مكالمة Azure Communication Services 1:1، مكالمة هاتفية 1:1، مكالمة مجموعة خدمات اتصالات Azure، الانضمام إلى اجتماع Teams، إلخ).

private async Task<CommunicationCall> StartAcsCallAsync(string acsCallee)
{
    var options = await GetStartCallOptionsAsynnc();
    var call = await this.callAgent.StartCallAsync( new [] { new UserCallIdentifier(acsCallee) }, options);
    return call;
}

private async Task<CommunicationCall> StartPhoneCallAsync(string acsCallee, string alternateCallerId)
{
    var options = await GetStartCallOptionsAsynnc();
    options.AlternateCallerId = new PhoneNumberCallIdentifier(alternateCallerId);

    var call = await this.callAgent.StartCallAsync( new [] { new PhoneNumberCallIdentifier(acsCallee) }, options);
    return call;
}

private async Task<CommunicationCall> JoinGroupCallByIdAsync(Guid groupId)
{
    var joinCallOptions = await GetJoinCallOptionsAsync();

    var groupCallLocator = new GroupCallLocator(groupId);
    var call = await this.callAgent.JoinAsync(groupCallLocator, joinCallOptions);
    return call;
}

private async Task<CommunicationCall> JoinTeamsMeetingByLinkAsync(Uri teamsCallLink)
{
    var joinCallOptions = await GetJoinCallOptionsAsync();

    var teamsMeetingLinkLocator = new TeamsMeetingLinkLocator(teamsCallLink.AbsoluteUri);
    var call = await callAgent.JoinAsync(teamsMeetingLinkLocator, joinCallOptions);
    return call;
}

private async Task<StartCallOptions> GetStartCallOptionsAsynnc()
{
    return new StartCallOptions() {
        OutgoingAudioOptions = new OutgoingAudioOptions() { IsOutgoingAudioMuted = true, OutgoingAudioStream = micStream  },
        OutgoingVideoOptions = new OutgoingVideoOptions() { OutgoingVideoStreams = new OutgoingVideoStream[] { cameraStream } }
    };
}

private async Task<JoinCallOptions> GetJoinCallOptionsAsync()
{
    return new JoinCallOptions() {
        OutgoingAudioOptions = new OutgoingAudioOptions() { IsOutgoingAudioMuted = true },
        OutgoingVideoOptions = new OutgoingVideoOptions() { OutgoingVideoStreams = new OutgoingVideoStream[] { cameraStream } }
    };
}

أضف التعليمات البرمجية لإنشاء LocalVideoStream اعتمادا على الكاميرا المحددة على CameraList_SelectionChanged الأسلوب .

var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
cameraStream = new LocalOutgoingVideoStream(selectedCamerea);

 var localUri = await cameraStream.StartPreviewAsync();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
    LocalVideo.Source = MediaSource.CreateFromUri(localUri);
});

if (call != null)
{
    await call?.StartVideoAsync(cameraStream);
}

قبول مكالمة واردة

أضف التنفيذ إلى OnIncomingCallAsync للرد على مكالمة واردة بالفيديو، ومرر LocalVideoStream إلى acceptCallOptions.

var incomingCall = args.IncomingCall;

var acceptCallOptions = new AcceptCallOptions() { 
    IncomingVideoOptions = new IncomingVideoOptions()
    {
        IncomingVideoStreamKind = VideoStreamKind.RemoteIncoming
    } 
};

_ = await incomingCall.AcceptAsync(acceptCallOptions);

مشارك عن بُعد ودفق الفيديو عن بُعد

يتوفر جميع المشاركين عن بُعد من خلال RemoteParticipantsالمجموعة في مثيل مكالمة. بمجرد توصيل المكالمة، يمكننا الوصول إلى المشاركين عن بعد في المكالمة والتعامل مع تدفقات الفيديو البعيدة.


private async void Call_OnVideoStreamsUpdatedAsync(object sender, RemoteVideoStreamsEventArgs args)
{
    foreach (var remoteVideoStream in args.AddedRemoteVideoStreams)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
        {
            RemoteVideo.Source = await remoteVideoStream.Start();
        });
    }

    foreach (var remoteVideoStream in args.RemovedRemoteVideoStreams)
    {
        remoteVideoStream.Stop();
    }
}

private async void Agent_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 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;
    }
}

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

}

تقديم ملفات الفيديو عن بعد

لكل دفق فيديو عن بعد، قم بإرفاقه بجهاز MediaElement .

private async Task AddVideoStreamsAsync(IReadOnlyList<RemoteVideoStream> remoteVideoStreams)
{
    foreach (var remoteVideoStream in remoteVideoStreams)
    {
        var remoteUri = await remoteVideoStream.Start();

        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            RemoteVideo.Source = remoteUri;
            RemoteVideo.Play();
        });
    }
}

تحديث حالة الاتصال

نحتاج إلى تنظيف أجهزة عرض الفيديو بمجرد قطع الاتصال ومعالجة الحالة عندما ينضم المشاركون عن بعد إلى المكالمة في البداية.

private async void Call_OnStateChanged(object sender, PropertyChangedEventArgs args)
{
    switch (((Call)sender).State)
    {
        case CallState.Disconnected:
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                LocalVideo.Source = null;
                RemoteVideo.Source = null;
            });
            break;

        case CallState.Connected:
            foreach (var remoteParticipant in call.RemoteParticipants)
            {
                String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
                remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
                await AddVideoStreams(remoteParticipant.VideoStreams);
                remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdated;
            }
            break;

        default:
            break;
    }
}

إنهاء مكالمة

قم بإنهاء المكالمة الحالية عند النقر فوق الزر Hang Up. أضف التنفيذ إلى HangupButton_Click لإنهاء مكالمة باستخدام callAgent الذي أنشأناه، وهدم تحديث المشارك ومعالجات أحداث حالة الاتصال.

var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
    try
    {
        await call.HangUpAsync(new HangUpOptions() { ForEveryone = true });
    }
    catch(Exception ex) 
    {
    }
}

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

يمكنك إنشاء التعليمات البرمجية وتشغيلها على Visual Studio. بالنسبة إلى الأنظمة الأساسية للحلول، ندعم ARM64و x64 و x86.

يمكنك إجراء مكالمة فيديو صادرة عن طريق توفير معرف مستخدم في حقل النص والنقر فوق الزر Start Call.

ملاحظة: يؤدي الاتصال 8:echo123 إلى إيقاف دفق الفيديو لأن روبوت echo لا يدعم دفق الفيديو.

لمزيدٍ من المعلومات حول معرفات المستخدم (الهوية)، تحقق من دليل رموز الوصول المميزة للمستخدم.

نموذج التعليمات البرمجية ل WinUI 3

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

لإكمال هذا البرنامج التعليمي، تحتاج إلى المتطلبات الأساسية التالية:

الإعداد

إنشاء المشروع

في Visual Studio، قم بإنشاء مشروع جديد باستخدام قالب Blank App، Packaged (WinUI 3 في سطح المكتب) لإعداد تطبيق WinUI 3 من صفحة واحدة.

لقطة شاشة تعرض نافذة New WinUI Project داخل Visual Studio.

تثبيت الحزمة

انقر بزر الماوس الأيمن فوق المشروع وانتقل إلى Manage Nuget Packages لتثبيت Azure.Communication.Calling.WindowsClientالإصدار 1.0.0 أو الإصدار المتفوق. تأكد من تحديد Include Preleased.

طلب الإذن بالوصول

لقطة شاشة تظهر طلب الوصول إلى الإنترنت والميكروفون في Visual Studio.

أضف التعليمات البرمجية التالية إلى app.manifest:

<file name="RtmMvrMf.dll">
    <activatableClass name="VideoN.VideoSchemeHandler" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>

إعداد إطار عمل التطبيق

نحن بحاجة إلى تكوين تخطيط أساسي لإرفاق منطقنا. من أجل إجراء مكالمة صادرة، نحتاج إلى TextBox توفير معرف المستخدم للمتصل. نحتاج أيضًا إلى زر Start Call وزر Hang Up. نحتاج أيضًا إلى معاينة الفيديو المحلي وعرض الفيديو عن بُعد للمشارك الآخر. لذلك نحن بحاجة إلى عنصرين لعرض تيارات الفيديو.

افتح MainWindow.xaml، واستبدل المحتوى بالتنفيذ التالي:

<Page
    x:Class="CallingQuickstart.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CallingQuickstart"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid x:Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="32"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="200*"/>
            <RowDefinition Height="60*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" x:Name="AppTitleBar" Background="LightSeaGreen">
            <!-- Width of the padding columns is set in LayoutMetricsChanged handler. -->
            <!-- Using padding columns instead of Margin ensures that the background paints the area under the caption control buttons (for transparent buttons). -->
            <TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="4,4,0,0"/>
        </Grid>

        <TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" />

        <Grid Grid.Row="2" Background="LightGray">
            <Grid.RowDefinitions>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
            <MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
        </Grid>
        <StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
            <StackPanel Orientation="Horizontal" Margin="10">
                <TextBlock VerticalAlignment="Center">Cameras:</TextBlock>
                <ComboBox x:Name="CameraList" HorizontalAlignment="Left" Grid.Column="0" DisplayMemberPath="Name" SelectionChanged="CameraList_SelectionChanged" Margin="10"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
                <Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
                <CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
                <CheckBox x:Name="BackgroundBlur" Content="Background blur" Width="142" Margin="10,0,0,0" Click="BackgroundBlur_Click"/>
            </StackPanel>
        </StackPanel>
        <TextBox Grid.Row="4" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
    </Grid>    
</Page>

افتح App.xaml.cs (انقر بزر الماوس الأيمن واختر عرض التعليمات البرمجية) وأضف هذا السطر إلى الأعلى:

using CallingQuickstart;

افتح MainWindow.xaml.cs(انقر بزر الماوس الأيمن واختر عرض الرمز) واستبدل المحتوى بالتنفيذ التالي:

using Azure.Communication.Calling.WindowsClient;
using Azure.WinRT.Communication;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Media.Core;

namespace CallingQuickstart
{
    public sealed partial class MainWindow : Window
    {
        CallAgent callAgent;
        Call call;
        DeviceManager deviceManager;
        Dictionary<string, RemoteParticipant> remoteParticipantDictionary = new Dictionary<string, RemoteParticipant>();

        public MainWindow()
        {
            this.InitializeComponent();
            Task.Run(() => this.InitCallAgentAndDeviceManagerAsync()).Wait();
        }

        private async Task InitCallAgentAndDeviceManagerAsync()
        {
            // Initialize call agent and Device Manager
        }

        private async void Agent_OnIncomingCallAsync(object sender, IncomingCall incomingCall)
        {
            // Accept an incoming call
        }

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

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

        private async void Call_OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
        {
            var state = (sender as Call)?.State;
            this.DispatcherQueue.TryEnqueue(() => {
                State.Text = state.ToString();
            });
        }
    }
}

نموذج الكائن

تتعامل الفئات والواجهات التالية مع بعض الميزات الرئيسية لـAzure Communication Services Calling SDK:

Name ‏‏الوصف
CallClient CallClient هو نقطة الإدخال الرئيسية إلى مكتبة عميل الاتصال.
CallAgent CallAgent يتم استخدام لبدء المكالمات والانضمام إليها.
CommunicationCall CommunicationCall يتم استخدام لإدارة المكالمات الموضوعة أو المنضمة.
CallTokenCredential CallTokenCredential يتم استخدام كبيانات اعتماد الرمز المميز لإنشاء مثيل .CallAgent
CommunicationUserIdentifier CommunicationUserIdentifier يتم استخدام لتمثيل هوية المستخدم، والتي يمكن أن تكون أحد الخيارات التالية: CommunicationUserIdentifierأو PhoneNumberIdentifier .CallingApplication

مصادقة العميل

لتهيئة CallAgent، تحتاج إلى رمز مميز لوصول المستخدم. بشكل عام يتم إنشاء هذا الرمز المميز من خدمة مع مصادقة خاصة بالتطبيق. لمزيد من المعلومات حول الرموز المميزة لوصول المستخدم، تحقق من دليل الرموز المميزة لوصول المستخدم.

لبدء التشغيل السريع، استبدل <AUTHENTICATION_TOKEN> برمز الوصول المميز لمستخدم تم إنشاؤه لمورد Azure Communication Service.

بمجرد أن يكون لديك رمز مميز، قم بتهيئة CallAgent مثيل معه، ما يمكننا من إجراء المكالمات وتلقيها. من أجل الوصول إلى الكاميرات على الجهاز، نحتاج أيضا إلى الحصول على مثيل إدارة الأجهزة.

أضف الرمز التالي إلى دالة InitCallAgentAndDeviceManagerAsync.

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.OnCallsUpdated += Agent_OnCallsUpdatedAsync;
this.callAgent.OnIncomingCall += Agent_OnIncomingCallAsync;

بدء مكالمة باستخدام الفيديو

أضف التنفيذ إلى CallButton_Click لبدء مكالمة باستخدام الفيديو. نحن بحاجة إلى تعداد الكاميرات مع مثيل مدير الجهاز وبناء LocalVideoStream . لبدء مكالمة بالفيديو، نحتاج إلى ضبط VideoOptions مع LocalVideoStream وتمريرها مع startCallOptions لتعيين الخيارات الأولية للمكالمة. من خلال إرفاق LocalVideoStream ب MediaPlayerElement، يمكننا رؤية معاينة الفيديو المحلي.

var startCallOptions = new StartCallOptions();

if (this.deviceManager.Cameras?.Count > 0)
{
    var videoDeviceInfo = this.deviceManager.Cameras?.FirstOrDefault();
    if (videoDeviceInfo != null)
    {
        var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
        cameraStream = new LocalOutgoingVideoStream(selectedCamerea);

        var localUri = await cameraStream.StartPreviewAsync();
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            LocalVideo.Source = MediaSource.CreateFromUri(localUri);
        });

        startCallOptions.VideoOptions = new OutgoingVideoOptions(new[] { cameraStream });
    }
}

var callees = new ICommunicationIdentifier[1]
{
    new CommunicationUserIdentifier(CalleeTextBox.Text.Trim())
};

this.call = await this.callAgent.StartCallAsync(callees, startCallOptions);
this.call.OnRemoteParticipantsUpdated += Call_OnRemoteParticipantsUpdatedAsync;
this.call.OnStateChanged += Call_OnStateChangedAsync;

قبول مكالمة واردة

أضف التنفيذ إلى Agent_OnIncomingCallAsync للرد على مكالمة واردة بالفيديو، ومرر LocalVideoStream إلى acceptCallOptions.

var acceptCallOptions = new AcceptCallOptions();

if (this.deviceManager.Cameras?.Count > 0)
{
    var videoDeviceInfo = this.deviceManager.Cameras?.FirstOrDefault();
    if (videoDeviceInfo != null)
    {
        var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
        cameraStream = new LocalOutgoingVideoStream(selectedCamerea);

        var localUri = await cameraStream.StartPreviewAsync();
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            LocalVideo.Source = MediaSource.CreateFromUri(localUri);
        });

        acceptCallOptions.VideoOptions = new OutgoingVideoOptions(new[] { localVideoStream });
    }
}

call = await incomingCall.AcceptAsync(acceptCallOptions);

مشارك عن بُعد ودفق الفيديو عن بُعد

يتوفر جميع المشاركين عن بُعد من خلال RemoteParticipantsالمجموعة في مثيل مكالمة. بمجرد توصيل المكالمة، يمكننا الوصول إلى المشاركين عن بعد في المكالمة والتعامل مع تدفقات الفيديو البعيدة.

private async void Call_OnVideoStreamsUpdatedAsync(object sender, RemoteVideoStreamsEventArgs args)
{
    foreach (var remoteVideoStream in args.AddedRemoteVideoStreams)
    {
        this.DispatcherQueue.TryEnqueue(async () => {
            RemoteVideo.Source = MediaSource.CreateFromUri(await remoteVideoStream.Start());
            RemoteVideo.MediaPlayer.Play();
        });
    }

    foreach (var remoteVideoStream in args.RemovedRemoteVideoStreams)
    {
        remoteVideoStream.Stop();
    }
}

private async void Agent_OnCallsUpdatedAsync(object sender, CallsUpdatedEventArgs args)
{
    foreach (var call in args.AddedCalls)
    {
        foreach (var remoteParticipant in call.RemoteParticipants)
        {
            var remoteParticipantMRI = remoteParticipant.Identifier.ToString();
            this.remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
            await AddVideoStreamsAsync(remoteParticipant.VideoStreams);
            remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdatedAsync;
        }
    }
}

private async void Call_OnRemoteParticipantsUpdatedAsync(object sender, ParticipantsUpdatedEventArgs args)
{
    foreach (var remoteParticipant in args.AddedParticipants)
    {
        String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
        this.remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
        await AddVideoStreamsAsync(remoteParticipant.VideoStreams);
        remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdatedAsync;
    }

    foreach (var remoteParticipant in args.RemovedParticipants)
    {
        String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
        this.remoteParticipantDictionary.Remove(remoteParticipantMRI);
    }
}

تقديم ملفات الفيديو عن بعد

لكل دفق فيديو عن بعد، قم بإرفاقه بجهاز MediaPlayerElement .

private async Task AddVideoStreamsAsync(IReadOnlyList<RemoteVideoStream> remoteVideoStreams)
{
    foreach (var remoteVideoStream in remoteVideoStreams)
    {
        var remoteUri = await remoteVideoStream.Start();

        this.DispatcherQueue.TryEnqueue(() => {
            RemoteVideo.Source = MediaSource.CreateFromUri(remoteUri);
            RemoteVideo.MediaPlayer.Play();
        });
    }
}

تحديث حالة الاتصال

نحتاج إلى تنظيف أجهزة عرض الفيديو بمجرد قطع الاتصال ومعالجة الحالة عندما ينضم المشاركون عن بعد إلى المكالمة في البداية.

private async void Call_OnStateChanged(object sender, PropertyChangedEventArgs args)
{
    switch (((Call)sender).State)
    {
        case CallState.Disconnected:
            this.DispatcherQueue.TryEnqueue(() => { =>
            {
                LocalVideo.Source = null;
                RemoteVideo.Source = null;
            });
            break;

        case CallState.Connected:
            foreach (var remoteParticipant in call.RemoteParticipants)
            {
                String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
                remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
                await AddVideoStreams(remoteParticipant.VideoStreams);
                remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdated;
            }
            break;

        default:
            break;
    }
}

إنهاء مكالمة

قم بإنهاء المكالمة الحالية عند النقر فوق الزر Hang Up. أضف التنفيذ إلى HangupButton_Click لإنهاء مكالمة باستخدام callAgent الذي أنشأناه، وهدم تحديث المشارك ومعالجات أحداث حالة الاتصال.

this.call.OnRemoteParticipantsUpdated -= Call_OnRemoteParticipantsUpdatedAsync;
this.call.OnStateChanged -= Call_OnStateChangedAsync;
await this.call.HangUpAsync(new HangUpOptions());

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

يمكنك إنشاء التعليمات البرمجية وتشغيلها على Visual Studio. بالنسبة إلى الأنظمة الأساسية للحلول، ندعم ARM64و x64 و x86.

يمكنك إجراء مكالمة فيديو صادرة عن طريق توفير معرف مستخدم في حقل النص والنقر فوق الزر Start Call.

ملاحظة: يؤدي الاتصال 8:echo123 إلى إيقاف دفق الفيديو لأن روبوت echo لا يدعم دفق الفيديو.

لمزيدٍ من المعلومات حول معرفات المستخدم (الهوية)، تحقق من دليل رموز الوصول المميزة للمستخدم.