تفعيل تطبيق ويب من خلال البث

1- نظرة عامة

شعار Google Cast

سيعلّمك هذا الدرس التطبيقي حول الترميز كيفية تعديل تطبيق فيديو حالي على الويب لإرسال المحتوى على جهاز مزوّد بتكنولوجيا Google Cast.

ما المقصود بـ Google Cast؟

يتيح Google Cast للمستخدمين إرسال المحتوى من جهاز جوّال إلى جهاز تلفزيون. ويمكن للمستخدمين بعد ذلك استخدام أجهزتهم الجوّالة كوحدة تحكم عن بُعد لتشغيل الوسائط على التلفزيون.

تتيح لك حزمة Google Cast SDK توسيع نطاق تطبيقك للتحكّم في نظام التلفزيون أو الصوت. تتيح لك أدوات Cast Cast إضافة مكونات واجهة المستخدم الضرورية استنادًا إلى قائمة التحقق من Google Cast Design.

يتم توفير قائمة التحقق من تصميم Google Cast لتيسير تجربة المستخدم في Cast، كما يمكن التنبؤ بها عبر جميع الأنظمة الأساسية المتاحة.

ما الذي سنبنيه؟

عند الانتهاء من هذا الدرس التطبيقي حول الترميز، سيصبح لديك تطبيق فيديو Chrome على الويب سيكون قادرًا على إرسال الفيديوهات إلى جهاز Google Cast.

ما ستتعرَّف عليه

  • كيفية إضافة حزمة Google Cast SDK إلى نموذج فيديو.
  • كيفية إضافة زر الإرسال لاختيار جهاز Google Cast
  • كيفية الاتصال بجهاز بث وتشغيل جهاز استقبال الوسائط
  • كيفية إرسال فيديو.
  • كيفية دمج Cast Connect

المتطلبات

  • أحدث إصدار من متصفّح Google Chrome
  • خدمة استضافة HTTPS مثل استضافة Firebase أو ngrok.
  • جهاز Google Cast مثل Chromecast أو Android TV الذي تم إعداده للاستخدام مع الاتصال بالإنترنت.
  • تلفزيون أو شاشة مزودة بمنفذ إدخال HDMI.
  • يجب توفّر جهاز Chromecast مع Google TV لاختبار دمج Cast Connect، إلا أنّه اختياري لبقية أجزاء الدرس التطبيقي حول الترميز. إذا لم يكن لديك هذا البرنامج، يُرجى عدم التردد في تخطّي خطوة إضافة دعم "الربط بتكنولوجيا Google Cast" في نهاية هذا البرنامج التعليمي.

التجربة

  • ستحتاج إلى معرفة سابقة بتطوير الويب.
  • ستحتاج أيضًا إلى معرفة سابقة بمشاهدة التلفزيون :)

كيف ستستخدم هذا البرنامج التعليمي؟

قراءة فقط قراءة النموذج وإكمال التدريبات

ما هو تقييمك لتجربتك في إنشاء تطبيقات الويب؟

نوفمبر متوسط خبير

ما هو تقييمك لتجربتك في مشاهدة التلفزيون؟

نوفمبر متوسط محترف

2- الحصول على نموذج الشفرة

يمكنك تنزيل كل نماذج الشفرة إلى جهاز الكمبيوتر...

وفكّ ضغط الملف المضغوط الذي تم تنزيله.

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

شعار Google Chrome

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

ولتتمكّن من استخدام "مكتملة"، يجب استضافتها.

إذا لم يكن لديك خادم متاح للاستخدام، يمكنك استخدام استضافة Firebase أو ngrok.

تشغيل الخادم

بعد إعداد الخدمة التي تريدها، انتقل إلى app-done وابدأ تشغيل الخادم.

في المتصفح، يُرجى زيارة عنوان URL https للعينة التي تستضيفها.

  1. من المفترض أن يظهر تطبيق الفيديو.
  2. انقر على زر الإرسال وحدد جهاز Google Cast.
  3. اختَر فيديو، وانقر على زر التشغيل.
  4. سيبدأ تشغيل الفيديو على جهاز Google Cast.

صورة فيديو يتمّ تشغيله على جهاز بث

انقر على زر الإيقاف المؤقت في عنصر الفيديو لإيقاف الفيديو مؤقتًا على جهاز الاستقبال. انقر على زر التشغيل في عنصر الفيديو لمتابعة تشغيل الفيديو مرة أخرى.

انقر على زر الإرسال لإيقاف الإرسال إلى جهاز Google Cast.

قبل المتابعة، أوقف الخادم.

4. إعداد المشروع الافتتاحي

صورة فيديو يتمّ تشغيله على جهاز بث

يجب إضافة Google Cast إلى بدء تشغيل التطبيق الذي نزّلته. في ما يلي بعض مصطلحات Google Cast التي سنستخدمها في هذا الدرس التطبيقي حول الترميز:

  • يعمل تطبيق المُرسِل على جهاز جوّال أو كمبيوتر محمول،
  • يعمل تطبيق مستلِم على جهاز Google Cast.

أنت الآن مستعد للبدء في العمل على مشروع المبتدئين باستخدام محرر النصوص المفضل لديك:

  1. اختَر الدليل رمز المجلدapp-start من نموذج تنزيل الرمز.
  2. شغّل التطبيق باستخدام الخادم واستكشف واجهة المستخدم.

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

تصميم التطبيقات

يجلب التطبيق قائمة بمقاطع الفيديو من خادم ويب بعيد ويوفر قائمة يتصفحها المستخدم. ويمكن للمستخدمين اختيار فيديو للاطّلاع على التفاصيل أو تشغيل الفيديو محليًا على الجهاز الجوّال.

يتكون التطبيق من عرض رئيسي واحد، محدد في index.html ووحدة التحكم الرئيسية، CastVideos.js.

index.html

يعلن ملف html هذا عن جميع واجهات مستخدم تطبيق الويب تقريبًا.

هناك أقسام قليلة من المشاهدات، وهي تحتوي على div#main_video التي تحتوي على عنصر الفيديو. بالنسبة إلى عنصر div للفيديو، لدينا div#media_control تحدد كل عناصر التحكم في عنصر الفيديو. يظهر القسم media_info أسفل منه، ويعرض تفاصيل الفيديو المعروض. وأخيرًا، يعرض عنصر div carousel قائمة بالفيديوهات في علامة div.

يُشغِّل ملف index.html أيضًا حزمة تطوير البرامج (SDK) للإرسال ويخبر الدالة CastVideos بالتحميل.

ويتم تحديد معظم المحتوى الذي سيملأ هذه العناصر وإدخاله والتحكم فيه في CastVideos.js. لنلقِ نظرة على ذلك.

VideoVideos.js

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

هناك بعض الأقسام الرئيسية المسؤولة عن إدارة الفيديو وتشغيله محليًا وبعيدًا عن بُعد. وبوجه عام، يعد هذا تطبيق ويب مباشرًا نوعًا ما.

CastPlayer هي الفئة الرئيسية التي تدير التطبيق بالكامل، وهي إعداد المشغّل واختيار الوسائط وربط الأحداث بـ PlayerHandler لتشغيل الوسائط. CastPlayer.prototype.initializeCastPlayer هي الطريقة التي تعمل على إعداد جميع وظائف الإرسال. يعمل CastPlayer.prototype.switchPlayer على تبديل الحالة بين المشغّلات المحلية والمشغّلة عن بُعد. يهيئ CastPlayer.prototype.setupLocalPlayer وCastPlayer.prototype.setupRemotePlayer اللاعبين المحليين والبعيدين.

PlayerHandler هي الفئة المسؤولة عن إدارة تشغيل الوسائط. وهناك عدد من الطرق الأخرى المسؤولة عن تفاصيل إدارة الوسائط والتشغيل.

الأسئلة الشائعة

5. إضافة زر الإرسال

صورة تطبيق يعمل بتكنولوجيا Google Cast

يعرض التطبيق الذي يعمل بتكنولوجيا Google Cast زر الإرسال في عنصر الفيديو. يؤدي النقر على الزر "إرسال" إلى عرض قائمة بأجهزة البث التي يمكن للمستخدم اختيارها. إذا كان المستخدم يشغِّل المحتوى محليًا على جهاز المُرسِل، سيؤدي اختيار جهاز بث إلى بدء التشغيل على جهاز البث أو استئناف تشغيله. في أي وقت أثناء جلسة الإرسال، يمكن للمستخدم النقر على زر الإرسال وإيقاف إرسال التطبيق إلى جهاز البث. يجب أن يكون المستخدم قادرًا على الاتصال بجهاز البث أو قطع الاتصال به أثناء استخدام أي شاشة في تطبيقك، كما هو موضح في قائمة التحقق من تصميم Google Cast.

الإعدادات

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

في متصفّحك، يُرجى الانتقال إلى عنوان URL الخاص بالنموذج https الذي تستضيفه.

تذكر أنه أثناء إجراء التغييرات، يجب إعادة استضافة النموذج على خادمك تبعًا للخدمة.

الإعداد

يحتوي إطار عمل Cast على كائن سينغلتون فردي، CastContext، ينسق بين جميع أنشطة إطار العمل. يجب إعداد هذا الكائن في مرحلة مبكرة من دورة حياة التطبيق، وعادة ما يتم استدعاؤه من معاودة الاتصال المعيّنة إلى window['__onGCastApiAvailable']، ويتم استدعاء هذا الكائن بعد تحميل حزمة تطوير البرامج (SDK) للإرسال، ويصبح متاحًا للاستخدام. في هذه الحالة، يتم استدعاء CastContext في CastPlayer.prototype.initializeCastPlayer، وهو ما يتم استدعاؤه من معاودة الاتصال المذكورة أعلاه.

يجب تقديم كائن JSON options عند إعداد CastContext. تتضمن هذه الفئة خيارات تؤثر على سلوك إطار العمل. أهم هذه الطرق هو معرِّف تطبيق المُستلِم، والذي يُستخدَم لفلترة قائمة أجهزة البث المتاحة لعرض الأجهزة القادرة على تشغيل التطبيق المُحدَّد فقط ولإطلاق تطبيق المُستلِم عند بدء جلسة إرسال.

عند تطوير تطبيقك الذي يستخدم تكنولوجيا Google Cast، يتعين عليك التسجيل كمطوّر برامج Google Cast ثم الحصول على معرّف تطبيق لتطبيقك. وبالنسبة إلى هذا الدرس التطبيقي حول الترميز، سنستخدم نموذج رقم تعريف التطبيق.

إضافة الرمز التالي إلى index.html في نهاية القسم body:

<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>

أضِف الرمز التالي إلى index.html لإعداد تطبيق CastVideos، بالإضافة إلى إعداد CastContext:

<script src="CastVideos.js"></script>
<script type="text/javascript">
var castPlayer = new CastPlayer();
window['__onGCastApiAvailable'] = function(isAvailable) {
  if (isAvailable) {
    castPlayer.initializeCastPlayer();
  }
};
</script>

الآن، نحتاج إلى إضافة طريقة جديدة في CastVideos.js، تتوافق مع الطريقة التي اتصلنا بها للتو في index.html. لنضف طريقة جديدة، تسمى initializeCastPlayer، تعمل على تعيين الخيارات في CastContext، وإعداد RemotePlayer وRemotePlayerControllers الجديدين:

/**
 * This method sets up the CastContext, and a few other members
 * that are necessary to play and control videos on a Cast
 * device.
 */
CastPlayer.prototype.initializeCastPlayer = function() {

    var options = {};

    // Set the receiver application ID to your own (created in
    // the Google Cast Developer Console), or optionally
    // use the chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
    options.receiverApplicationId = 'C0868879';

    // Auto join policy can be one of the following three:
    // ORIGIN_SCOPED - Auto connect from same appId and page origin
    // TAB_AND_ORIGIN_SCOPED - Auto connect from same appId, page origin, and tab
    // PAGE_SCOPED - No auto connect
    options.autoJoinPolicy = chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED;

    cast.framework.CastContext.getInstance().setOptions(options);

    this.remotePlayer = new cast.framework.RemotePlayer();
    this.remotePlayerController = new cast.framework.RemotePlayerController(this.remotePlayer);
    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,
        this.switchPlayer.bind(this)
    );
};

وأخيرًا، يجب إنشاء المتغيّرات لـ RemotePlayer وRemotePlayerController:

var CastPlayer = function() {
  //...
  /* Cast player variables */
  /** @type {cast.framework.RemotePlayer} */
  this.remotePlayer = null;
  /** @type {cast.framework.RemotePlayerController} */
  this.remotePlayerController = null;
  //...
};

زر الإرسال

الآن وبعد إعداد CastContext، يلزم إضافة زر الإرسال للسماح للمستخدم باختيار جهاز بث. توفر حزمة تطوير البرامج (SDK) للإرسال مكوِّن زر الإرسال المسمّى google-cast-launcher بمعرّف "castbutton"". ويمكن إضافته إلى عنصر الفيديو في التطبيق من خلال إضافة button في القسم media_control.

هذا هو شكل عنصر الزر:

<google-cast-launcher id="castbutton"></google-cast-launcher>

إضافة الرمز التالي إلى index.html في القسم media_control:

<div id="media_control">
  <div id="play"></div>
  <div id="pause"></div>
  <div id="progress_bg"></div>
  <div id="progress"></div>
  <div id="progress_indicator"></div>
  <div id="fullscreen_expand"></div>
  <div id="fullscreen_collapse"></div>
  <google-cast-launcher id="castbutton"></google-cast-launcher>
  <div id="audio_bg"></div>
  <div id="audio_bg_track"></div>
  <div id="audio_indicator"></div>
  <div id="audio_bg_level"></div>
  <div id="audio_on"></div>
  <div id="audio_off"></div>
  <div id="duration">00:00:00</div>
</div>

والآن أعِد تحميل الصفحة في متصفّح Chrome. من المفترض أن يظهر لك زر الإرسال في عنصر الفيديو وعند النقر عليه، سيتم عرض أجهزة البث على شبكتك المحلية. تتم إدارة اكتشاف الجهاز تلقائيًا بواسطة متصفح Chrome. اختَر جهاز البث وسيتم تحميل نموذج تطبيق المُستلِم على جهاز البث.

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

6. إرسال محتوى الفيديو

صورة تطبيق مزوّد بتكنولوجيا Google Cast مع قائمة تحديد جهاز البث

سيتم توسيع نموذج التطبيق لتشغيل مقاطع الفيديو عن بُعد أيضًا على جهاز بث. ولإجراء ذلك، نحتاج إلى الاستماع إلى الأحداث المختلفة التي يتم إنشاؤها بواسطة إطار عمل الإرسال.

بث الوسائط

على مستوى عالٍ، إذا أردت تشغيل وسائط على جهاز بث، يجب تنفيذ ما يلي:

  1. أنشئ كائن MediaInfo JSON من Cast SDK الذي يصمِّم عنصر وسائط.
  2. يتصل المستخدم بجهاز البث لتشغيل تطبيق الاستقبال.
  3. حمِّل الكائن MediaInfo في جهاز الاستقبال وشغِّل المحتوى.
  4. تتبع حالة الوسائط.
  5. إرسال أوامر التشغيل إلى المُستلِم بناءً على تفاعلات المستخدم.

تكمن الخطوة الأولى في تعيين كائن إلى عنصر آخر، وهو MediaInfo الذي يفهمه تطبيق Cast SDK وmediaJSON هو غلاف تطبيقنا لعنصر الوسائط. ويمكننا بسهولة ربط mediaJSON بـ MediaInfo. لقد انتهينا من الخطوة 2 في القسم السابق. من السهل تنفيذ الخطوة 3 باستخدام Cast SDK.

يفرّق نموذج التطبيق CastPlayer بين التشغيل المحلي والتشغيل عن بُعد باستخدام طريقة switchPlayer:

if (cast && cast.framework) {
  if (this.remotePlayer.isConnected) {
    //...

ليس من المهم في هذا الدرس التطبيقي حول الترميز أن تفهم بالضبط آلية عمل منطق كل نماذج اللاعبين. ومع ذلك، فمن المهم أن تفهم أنه يجب تعديل مشغّل الوسائط لتطبيقك ليكون على دراية بكل من التشغيل المحلي والبعيد.

في الوقت الحالي، يكون المشغل المحلي دائمًا في حالة التشغيل المحلية لأنه لا يعرف أي شيء عن حالات الإرسال حتى الآن. نحتاج إلى تحديث واجهة المستخدم استنادًا إلى انتقالات الحالة التي تحدث في إطار عمل الإرسال. على سبيل المثال، إذا بدأنا الإرسال، نحتاج إلى إيقاف التشغيل المحلي وإيقاف بعض عناصر التحكّم. وبالمثل، إذا توقفنا عن الإرسال عندما نكون في وحدة التحكم في طريقة العرض هذه، سنحتاج إلى الانتقال إلى التشغيل المحلي. ولمعالجة ذلك، نحتاج إلى الاستماع إلى الأحداث المختلفة التي يتم إنشاؤها بواسطة إطار عمل الإرسال.

إدارة جلسة الإرسال

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

سيتم بدء جلسة الإرسال تلقائيًا عندما يختار المستخدم جهازًا من زر الإرسال، وسيتم إيقافها تلقائيًا عند قطع اتصال المستخدم. يتم أيضًا التعامل مع إعادة الاتصال بجلسة المُستلِم بسبب مشاكل في الشبكة تلقائيًا من خلال إطار عمل الإرسال.

تتم إدارة جلسات الإرسال بواسطة CastSession، الذي يمكن الوصول إليه عبر cast.framework.CastContext.getInstance().getCurrentSession(). يمكن استخدام استدعاءات EventListener لمراقبة أحداث الجلسة، مثل الإنشاء والتعليق والاستئناف والإنهاء.

في تطبيقنا الحالي، يتم التعامل مع إدارة الجلسة والجلسة بالكامل بالنسبة إلينا باستخدام طريقة setupRemotePlayer. لنبدأ بإعداد ذلك في تطبيقك من خلال إضافة الرمز التالي إلى CastVideos.js:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    var castSession = cast.framework.CastContext.getInstance().getCurrentSession();

    this.playerHandler.setTarget(playerTarget);

    // Setup remote player volume right on setup
    // The remote player may have had a volume set from previous playback
    if (this.remotePlayer.isMuted) {
        this.playerHandler.mute();
    }
    var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
    var p = document.getElementById('audio_bg_level');
    p.style.height = currentVolume + 'px';
    p.style.marginTop = -currentVolume + 'px';

    this.hideFullscreenButton();

    this.playerHandler.play();
};

ما زلنا نحتاج إلى ربط جميع الأحداث من عمليات معاودة الاتصال ومعالجة جميع الأحداث الواردة. هذا أمر بسيط إلى حد ما، لذا دعنا نتولى ذلك الآن:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    var castSession = cast.framework.CastContext.getInstance().getCurrentSession();

    // Add event listeners for player changes which may occur outside sender app
    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED,
        function() {
            if (this.remotePlayer.isPaused) {
                this.playerHandler.pause();
            } else {
                this.playerHandler.play();
            }
        }.bind(this)
    );

    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_MUTED_CHANGED,
        function() {
            if (this.remotePlayer.isMuted) {
                this.playerHandler.mute();
            } else {
                this.playerHandler.unMute();
            }
        }.bind(this)
    );

    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.VOLUME_LEVEL_CHANGED,
        function() {
            var newVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
            var p = document.getElementById('audio_bg_level');
            p.style.height = newVolume + 'px';
            p.style.marginTop = -newVolume + 'px';
        }.bind(this)
    );

    // This object will implement PlayerHandler callbacks with
    // remotePlayerController, and makes necessary UI updates specific
    // to remote playback
    var playerTarget = {};

    playerTarget.play = function () {
        if (this.remotePlayer.isPaused) {
            this.remotePlayerController.playOrPause();
        }

        var vi = document.getElementById('video_image');
        vi.style.display = 'block';
        var localPlayer = document.getElementById('video_element');
        localPlayer.style.display = 'none';
    }.bind(this);

    playerTarget.pause = function () {
        if (!this.remotePlayer.isPaused) {
            this.remotePlayerController.playOrPause();
        }
    }.bind(this);

    playerTarget.stop = function () {
         this.remotePlayerController.stop();
    }.bind(this);

    playerTarget.getCurrentMediaTime = function() {
        return this.remotePlayer.currentTime;
    }.bind(this);

    playerTarget.getMediaDuration = function() {
        return this.remotePlayer.duration;
    }.bind(this);

    playerTarget.updateDisplayMessage = function () {
        document.getElementById('playerstate').style.display = 'block';
        document.getElementById('playerstatebg').style.display = 'block';
        document.getElementById('video_image_overlay').style.display = 'block';
        document.getElementById('playerstate').innerHTML =
            this.mediaContents[ this.currentMediaIndex]['title'] + ' ' +
            this.playerState + ' on ' + castSession.getCastDevice().friendlyName;
    }.bind(this);

    playerTarget.setVolume = function (volumeSliderPosition) {
        // Add resistance to avoid loud volume
        var currentVolume = this.remotePlayer.volumeLevel;
        var p = document.getElementById('audio_bg_level');
        if (volumeSliderPosition < FULL_VOLUME_HEIGHT) {
            var vScale =  this.currentVolume * FULL_VOLUME_HEIGHT;
            if (volumeSliderPosition > vScale) {
                volumeSliderPosition = vScale + (pos - vScale) / 2;
            }
            p.style.height = volumeSliderPosition + 'px';
            p.style.marginTop = -volumeSliderPosition + 'px';
            currentVolume = volumeSliderPosition / FULL_VOLUME_HEIGHT;
        } else {
            currentVolume = 1;
        }
        this.remotePlayer.volumeLevel = currentVolume;
        this.remotePlayerController.setVolumeLevel();
    }.bind(this);

    playerTarget.mute = function () {
        if (!this.remotePlayer.isMuted) {
            this.remotePlayerController.muteOrUnmute();
        }
    }.bind(this);

    playerTarget.unMute = function () {
        if (this.remotePlayer.isMuted) {
            this.remotePlayerController.muteOrUnmute();
        }
    }.bind(this);

    playerTarget.isMuted = function() {
        return this.remotePlayer.isMuted;
    }.bind(this);

    playerTarget.seekTo = function (time) {
        this.remotePlayer.currentTime = time;
        this.remotePlayerController.seek();
    }.bind(this);

    this.playerHandler.setTarget(playerTarget);

    // Setup remote player volume right on setup
    // The remote player may have had a volume set from previous playback
    if (this.remotePlayer.isMuted) {
        this.playerHandler.mute();
    }
    var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
    var p = document.getElementById('audio_bg_level');
    p.style.height = currentVolume + 'px';
    p.style.marginTop = -currentVolume + 'px';

    this.hideFullscreenButton();

    this.playerHandler.play();
};

جارٍ تحميل الوسائط

في حزمة Cast Cast، توفِّر RemotePlayer وRemotePlayerController مجموعة من واجهات برمجة التطبيقات المناسبة لإدارة تشغيل الوسائط عن بُعد على جهاز الاستقبال. بالنسبة إلى CastSession الذي يدعم تشغيل الوسائط، سيتم إنشاء مثيلات RemotePlayer وRemotePlayerController تلقائيًا بواسطة SDK. ويمكن الوصول إليها من خلال إنشاء مثيلات للسمتَين cast.framework.RemotePlayer وcast.framework.RemotePlayerController على التوالي، كما هو موضّح سابقًا في الدرس التطبيقي حول الترميز.

بعد ذلك، نحتاج إلى تحميل الفيديو المحدد حاليًا على المتلقي من خلال إنشاء كائن MediaInfo لحزمة SDK لمعالجة الطلب وتمريره. يُرجى إضافة الرمز التالي إلى setupRemotePlayer لإجراء ذلك:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    //...

    playerTarget.load = function (mediaIndex) {
        console.log('Loading...' + this.mediaContents[mediaIndex]['title']);
        var mediaInfo = new chrome.cast.media.MediaInfo(
            this.mediaContents[mediaIndex]['sources'][0], 'video/mp4');

        mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
        mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
        mediaInfo.metadata.title = this.mediaContents[mediaIndex]['title'];
        mediaInfo.metadata.images = [
            {'url': MEDIA_SOURCE_ROOT + this.mediaContents[mediaIndex]['thumb']}];

        var request = new chrome.cast.media.LoadRequest(mediaInfo);
        castSession.loadMedia(request).then(
            this.playerHandler.loaded.bind(this.playerHandler),
            function (errorCode) {
                this.playerState = PLAYER_STATE.ERROR;
                console.log('Remote media load error: ' +
                    CastPlayer.getErrorMessage(errorCode));
            }.bind(this));
    }.bind(this);

    //...
};

يمكنك الآن إضافة طريقة للتبديل بين التشغيل المحلي والتشغيل عن بُعد:

/**
 * This is a method for switching between the local and remote
 * players. If the local player is selected, setupLocalPlayer()
 * is run. If there is a cast device connected we run
 * setupRemotePlayer().
 */
CastPlayer.prototype.switchPlayer = function() {
    this.stopProgressTimer();
    this.resetVolumeSlider();
    this.playerHandler.stop();
    this.playerState = PLAYER_STATE.IDLE;
    if (cast && cast.framework) {
        if (this.remotePlayer.isConnected) {
            this.setupRemotePlayer();
            return;
        }
    }
    this.setupLocalPlayer();
};

أخيرًا، أضف طريقة للتعامل مع أي رسائل خطأ في Cast:

/**
 * Makes human-readable message from chrome.cast.Error
 * @param {chrome.cast.Error} error
 * @return {string} error message
 */
CastPlayer.getErrorMessage = function(error) {
  switch (error.code) {
    case chrome.cast.ErrorCode.API_NOT_INITIALIZED:
      return 'The API is not initialized.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CANCEL:
      return 'The operation was canceled by the user' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CHANNEL_ERROR:
      return 'A channel to the receiver is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.EXTENSION_MISSING:
      return 'The Cast extension is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.INVALID_PARAMETER:
      return 'The parameters to the operation were not valid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.RECEIVER_UNAVAILABLE:
      return 'No receiver was compatible with the session request.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.SESSION_ERROR:
      return 'A session could not be created, or a session was invalid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.TIMEOUT:
      return 'The operation timed out.' +
        (error.description ? ' :' + error.description : '');
  }
};

الآن، شغّل التطبيق. اتصل بجهاز البث وابدأ تشغيل فيديو. من المفترض أن يظهر الفيديو قيد التشغيل على جهاز الاستقبال.

7. إضافة دعم Cast Connect

تسمح مكتبة Cast Connect لتطبيقات المرسِلين الحالية بالاتصال بتطبيقات Android TV عبر بروتوكول الإرسال. يعمل Cast Connect على البنية الأساسية لتطبيق Cast، حيث يعمل تطبيق Android TV كجهاز استقبال.

العناصر التابعة

  • الإصدار 87 أو الإصدارات الأحدث من متصفِّح Chrome

إعداد متوافق مع جهاز استقبال Android

لتشغيل تطبيق Android TV، الذي يُشار إليه أيضًا باسم مستلم Android، نحتاج إلى تعيين علامة androidReceiverCompatible إلى true في الكائن CastOptions.

أضِف الرمز التالي إلى CastVideos.js في الدالة initializeCastPlayer:

var options = {};
...
options.androidReceiverCompatible = true;

cast.framework.CastContext.getInstance().setOptions(options);

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

من جانب المرسل، يمكنك تحديد CredentialsData لتمثيل من ينضم إلى الجلسة. credentials عبارة عن سلسلة يمكن تحديدها بواسطة المستخدم، طالما يمكن لتطبيق ATV فهمها. لا يتم تمرير CredentialsData إلى تطبيق Android TV إلا أثناء وقت الإطلاق أو الانضمام. وإذا ضبطته مرة أخرى أثناء الاتصال، لن يتم تمريره إلى تطبيق Android TV.

لضبط بيانات اعتماد الإطلاق، يجب تحديد CredentialsData في أي وقت بعد ضبط خيارات التشغيل.

أضِف الرمز التالي إلى فئة CastVideos.js ضمن الدالة initializeCastPlayer:

cast.framework.CastContext.getInstance().setOptions(options);
...
let credentialsData = new chrome.cast.CredentialsData("{\"userId\": \"abc\"}");
cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData);
...

تعيين بيانات الاعتماد عند تحميل الطلب

في حال كان تطبيق "مستقبل الويب" وتطبيق Android TV يعالجان credentials بشكل مختلف، قد تحتاج إلى تحديد بيانات اعتماد منفصلة لكل منهما. لحل هذه المشكلة، أضف الرمز التالي في CastVideos.js ضمن playerTarget.load في الدالة setupRemotePlayer:

...
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.credentials = 'user-credentials';
request.atvCredentials = 'atv-user-credentials';
...

استنادًا إلى تطبيق المُستلِم الذي يُرسل المُرسِل إليه، ستتعامل حزمة تطوير البرامج (SDK) الآن مع بيانات الاعتماد المطلوب استخدامها للجلسة الحالية تلقائيًا.

جارٍ اختبار Cast Connect

خطوات تثبيت ملف APK لـ Android TV على جهاز Chromecast مع Google TV:

  1. ابحث عن عنوان IP لجهاز Android TV. وعادةً ما يكون هذا الإعداد متاحًا ضمن الإعدادات > الشبكة والإنترنت > (اسم الشبكة التي يتصل بها جهازك). على اليسار، سيتم عرض التفاصيل وعنوان IP لجهازك على الشبكة.
  2. استخدم عنوان IP لجهازك للاتصال به عبر ADB باستخدام الوحدة الطرفية:
$ adb connect <device_ip_address>:5555
  1. من نافذة الوحدة الطرفية، انتقِل إلى مجلد المستوى الأعلى للاطّلاع على نماذج التعليمات البرمجية التي نزّلتها في بداية هذا الدرس التطبيقي حول الترميز. مثلاً:
$ cd Desktop/chrome_codelab_src
  1. ثبِّت ملف apk .في هذا المجلد على Android TV من خلال تشغيل:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. من المفترض أن تتمكن الآن من رؤية تطبيق باسم إرسال مقاطع الفيديو في قائمة تطبيقاتك على جهاز Android TV.
  2. يمكنك تشغيل رمز مُرسل الويب المُحدَّث وإنشاء جلسة بث باستخدام جهاز Android TV باستخدام رمز الإرسال أو اختيار Cast.. من القائمة المنسدلة في متصفِّح Chrome. من المفترض أن يؤدي ذلك الآن إلى تشغيل تطبيق Android TV على جهاز استقبال Android والسماح لك بالتحكم في التشغيل باستخدام وحدة التحكم في جهاز Android TV عن بُعد.

8. تهانينا

أنت تعرف الآن كيفية تفعيل تطبيق فيديو يعمل بتكنولوجيا Google Cast باستخدام أدوات Cast SDK على تطبيق ويب من Chrome.

لمزيد من التفاصيل، راجع دليل مطوري برامج مرسل الويب.