הפעלת העברה (cast) של אפליקציית אינטרנט

1. סקירה כללית

הלוגו של Google Cast

ב-Codelab הזה תלמדו איך לשנות אפליקציית וידאו קיימת באינטרנט כדי להפעיל Cast של תוכן במכשיר שתומך ב-Google Cast.

מה זה Google Cast?

עם Google Cast, משתמשים יכולים להפעיל Cast של תוכן מנייד לטלוויזיה. לאחר מכן, המשתמשים יכולים להשתמש בנייד כשלט רחוק להפעלת מדיה בטלוויזיה.

ה-SDK של Google Cast מאפשר להרחיב את האפליקציה כדי לשלוט בטלוויזיה או במערכת שמע. ‫Cast SDK מאפשר לכם להוסיף את רכיבי ממשק המשתמש הנדרשים על סמך רשימת המשימות לעיצוב של Google Cast.

הכנו רשימת משימות לעיצוב Google Cast כדי שחוויית המשתמש תהיה פשוטה וצפויה בכל הפלטפורמות הנתמכות.

מה אנחנו הולכים לבנות?

בסיום ה-codelab הזה, תהיה לכם אפליקציית וידאו לאינטרנט ב-Chrome שתוכל להפעיל Cast של סרטונים למכשיר Cast.

מה תלמדו

  • איך מוסיפים את Google Cast SDK לאפליקציית וידאו לדוגמה.
  • איך מוסיפים את הכפתור להפעלת Cast כדי לבחור מכשיר Cast.
  • איך מתחברים למכשיר Cast ומפעילים מקלט מדיה
  • איך מפעילים Cast של סרטון.
  • איך משלבים את Cast Connect

הדרישות

  • הגרסה האחרונה של דפדפן Google Chrome.
  • שירות אירוח HTTPS כמו אירוח ב-Firebase או ngrok.
  • מכשיר Cast, כמו Chromecast או Android TV, שמחובר לאינטרנט.
  • טלוויזיה או צג עם כניסת HDMI.
  • כדי לבדוק את השילוב של Cast Connect, צריך Chromecast with Google TV, אבל הוא לא נדרש לשאר חלקי ה-Codelab. אם אין לכם כזה, אתם יכולים לדלג על השלב הוספת תמיכה ב-Cast Connect, לקראת סוף המדריך הזה.

חוויה

  • צריך ידע קודם בפיתוח אתרים.
  • תצטרכו גם ידע קודם בצפייה בטלוויזיה :)

איך תשתמשו במדריך הזה?

רק לקרוא לקרוא ולבצע את התרגילים

מה מידת שביעות הרצון שלך מחוויית בניית אפליקציות אינטרנט?

מתחילים ביניים מומחים

איך היית מדרג את חוויית הצפייה בטלוויזיה?

מתחילים ביניים מומחים

2. קבלת קוד לדוגמה

אפשר להוריד את כל קוד הדוגמה למחשב...

ומחלצים את קובץ ה-ZIP שהורד.

3. הרצת האפליקציה לדוגמה

הלוגו של Google Chrome

קודם נראה איך נראית אפליקציית הדוגמה המלאה. האפליקציה היא נגן וידאו בסיסי. המשתמש יכול לבחור סרטון מתוך רשימה ואז להפעיל אותו באופן מקומי במכשיר או להפעיל אותו באמצעות Cast במכשיר Google Cast.

כדי להשתמש בטופס המלא, צריך לארח אותו.

אם אין לכם שרת זמין לשימוש, אתם יכולים להשתמש באירוח ב-Firebase או ב-ngrok.

הפעלת השרת

אחרי שמגדירים את השירות הרצוי, עוברים אל app-done ומפעילים את השרת.

בדפדפן, נכנסים לכתובת ה-URL מסוג https של הדוגמה שאירחתם.

  1. אפליקציית הווידאו אמורה להופיע.
  2. לוחצים על הכפתור להפעלת Cast ובוחרים את מכשיר Cast.
  3. בוחרים סרטון ולוחצים על לחצן ההפעלה.
  4. הסרטון יתחיל לפעול במכשיר Cast.

תמונה של סרטון שמופעל במכשיר Cast

לוחצים על לחצן ההשהיה ברכיב הסרטון כדי להשהות את הסרטון במכשיר המקלט. לוחצים על לחצן ההפעלה ברכיב הווידאו כדי להמשיך בהפעלת הסרטון.

לוחצים על הכפתור להפעלת Cast כדי להפסיק את ההעברה למכשיר Cast של Google.

לפני שממשיכים, צריך לעצור את השרת.

4. הכנת פרויקט ההתחלה

תמונה של סרטון שמופעל במכשיר Cast

אנחנו צריכים להוסיף תמיכה ב-Google Cast לאפליקציית ההפעלה שהורדת. הנה כמה מונחים שקשורים ל-Google Cast שבהם נשתמש ב-codelab הזה:

  • אפליקציית השולח פועלת במכשיר נייד או במחשב נייד,
  • אפליקציית מקלט פועלת במכשיר Cast.

עכשיו אפשר להשתמש בפרויקט המתחיל כבסיס ולבנות עליו באמצעות כלי לעריכת טקסט המועדף:

  1. בוחרים את ספריית סמל תיקייהapp-start מתוך קובץ קוד לדוגמה שהורדתם.
  2. מריצים את האפליקציה באמצעות השרת ובוחנים את ממשק המשתמש.

הערה: במהלך העבודה על ה-codelab הזה, תצטרכו לארח מחדש את הדוגמה בשרת שלכם בהתאם לשירות.

עיצוב אפליקציות

האפליקציה מאחזרת רשימה של סרטונים משרת אינטרנט מרוחק ומספקת רשימה למשתמש כדי לעיין בה. המשתמשים יכולים לבחור סרטון כדי לראות את הפרטים שלו או להפעיל אותו באופן מקומי במכשיר הנייד.

האפליקציה מורכבת מתצוגה ראשית אחת, שמוגדרת ב-index.html ובבקר הראשי, CastVideos.js.

index.html

קובץ ה-HTML הזה מכריז על כמעט כל ממשק המשתמש של אפליקציית האינטרנט.

יש כמה קטעים של צפיות, יש לנו את div#main_video, שמכיל את רכיב הסרטון. בנוסף ל-div של הסרטון, יש לנו את div#media_control, שמגדיר את כל אמצעי הבקרה של רכיב הסרטון. מתחת לזה מופיע media_info, שבו מוצגים פרטי הסרטון שמוצג. לבסוף, התג carousel div מציג רשימה של סרטונים ב-div.

קובץ index.html גם מאתחל את Cast SDK ומורה לפונקציה CastVideos להיטען.

רוב התוכן שיוצג באלמנטים האלה מוגדר, מוזרק ונשלט ב-CastVideos.js. אז בואו נבדוק את זה.

CastVideos.js

הסקריפט הזה מנהל את כל הלוגיקה של אפליקציית האינטרנט Cast Videos. רשימת הסרטונים והמטא-נתונים המשויכים שלהם שמוגדרים ב-CastVideos.js כלולים באובייקט שנקרא mediaJSON.

יש כמה קטעים עיקריים שאחראים יחד על ניהול הסרטון והפעלתו באופן מקומי ומרחוק. באופן כללי, זו אפליקציית אינטרנט פשוטה למדי.

CastPlayer היא המחלקה הראשית שמנהלת את כל האפליקציה, מגדירה את נגן המדיה, בוחרת מדיה ומקשרת אירועים ל-PlayerHandler להפעלת מדיה. ‫CastPlayer.prototype.initializeCastPlayer היא השיטה שקובעת את כל הפונקציונליות של Cast. ‫CastPlayer.prototype.switchPlayer מחליף את המצב בין שחקנים מקומיים לשחקנים מרוחקים. ‫CastPlayer.prototype.setupLocalPlayer ו-CastPlayer.prototype.setupRemotePlayer מאתחלים שחקנים מקומיים ומרוחקים.

PlayerHandler היא המחלקה שאחראית לניהול הפעלת המדיה. יש עוד כמה שיטות שאחראיות על הפרטים של ניהול המדיה וההפעלה שלה.

שאלות נפוצות

5. הוספת הכפתור להפעלת Cast

תמונה של אפליקציה עם תמיכה ב-Cast

באפליקציה שתומכת ב-Cast, הכפתור להפעלת Cast מוצג ברכיב הווידאו. כשלוחצים על הכפתור להפעלת Cast, מוצגת רשימה של מכשירי Cast שהמשתמש יכול לבחור מתוכה. אם המשתמש הפעיל תוכן באופן מקומי במכשיר השולח, בחירה במכשיר Cast תתחיל או תמשיך את ההפעלה במכשיר ה-Cast. במהלך סשן Cast, המשתמש יכול ללחוץ על הכפתור להפעלת Cast ולהפסיק את ההעברה של האפליקציה למכשיר Cast. המשתמש צריך להיות מסוגל להתחבר למכשיר Cast או להתנתק ממנו מכל מסך באפליקציה, כמו שמתואר ברשימת המשימות לעיצוב Google Cast.

הגדרות אישיות

פרויקט ההתחלה דורש את אותם רכיבים תלויים והגדרות כמו באפליקציית הדוגמה המלאה, אבל הפעם צריך לארח את התוכן של app-start.

בדפדפן, עוברים לכתובת ה-URL של הדוגמה שאירחתם.https

חשוב לזכור: כשמבצעים שינויים, צריך לארח מחדש את הדוגמה בשרת, בהתאם לשירות.

אתחול

ל-Cast Framework יש אובייקט singleton גלובלי, CastContext, שמתאם את כל הפעילויות של ה-Framework. צריך לאתחל את האובייקט הזה בשלב מוקדם במחזור החיים של האפליקציה. בדרך כלל קוראים לו מתוך קריאה חוזרת שהוקצתה ל-window['__onGCastApiAvailable'], שמופעלת אחרי שה-Cast SDK נטען וזמין לשימוש. במקרה הזה, הפונקציה CastContext נקראת ב-CastPlayer.prototype.initializeCastPlayer, שנקראת מהקריאה החוזרת שצוינה למעלה.

כשמפעילים את CastContext, צריך לספק אובייקט JSON של options. הכיתה הזו מכילה אפשרויות שמשפיעות על ההתנהגות של המסגרת. הכי חשוב מביניהם הוא מזהה אפליקציית המקלט, שמשמש לסינון רשימת מכשירי Cast הזמינים כדי להציג רק מכשירים שיכולים להריץ את האפליקציה שצוינה, ולהפעיל את אפליקציית המקלט כשמתחילים סשן Cast.

כשמפתחים אפליקציה משלכם תומכת ב-Cast, צריך להירשם כמפתחי 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. נוסיף method חדש בשם 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;
  //...
};

כפתור הפעלת Cast

עכשיו, אחרי שהרכיב CastContext אותחל, צריך להוסיף את הכפתור להפעלת Cast כדי לאפשר למשתמש לבחור מכשיר Cast. ‫Cast SDK מספק רכיב של הכפתור להפעלת Cast שנקרא 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. כפתור Cast אמור להופיע ברכיב הסרטון. כשלוחצים עליו, מוצגת רשימה של מכשירי Cast ברשת המקומית. דפדפן Chrome מנהל את איתור המכשירים באופן אוטומטי. בוחרים את מכשיר ה-Cast, ואפליקציית המקלט לדוגמה תיטען במכשיר ה-Cast.

עדיין לא הוספנו תמיכה בהפעלת מדיה, כך שאי אפשר להפעיל סרטונים במכשיר Cast. לוחצים על הכפתור להפעלת Cast כדי להפסיק את ההעברה.

6. העברה (cast) של תוכן סרטון

תמונה של אפליקציה שמותאמת ל-Cast עם תפריט לבחירת מכשיר Cast

נרחיב את אפליקציית הדוגמה כך שתכלול גם הפעלת סרטונים מרחוק במכשיר Cast. כדי לעשות את זה, אנחנו צריכים להאזין לאירועים השונים שנוצרים על ידי Cast Framework.

הפעלת Cast של מדיה

באופן כללי, כדי להפעיל מדיה במכשיר Cast, צריך לבצע את הפעולות הבאות:

  1. יוצרים אובייקט MediaInfo JSON מ-Cast SDK שמדמה פריט מדיה.
  2. המשתמש מתחבר למכשיר Cast כדי להפעיל את אפליקציית המקלט.
  3. טוענים את אובייקט MediaInfo במכשיר המקבל ומפעילים את התוכן.
  4. מעקב אחרי סטטוס המדיה.
  5. שליחת פקודות הפעלה למקבל על סמך אינטראקציות של משתמשים.

בשלב 1 ממפים אובייקט אחד לאובייקט אחר. MediaInfo הוא אובייקט שמערכת Cast SDK מבינה, ו-mediaJSON הוא האובייקט שבו האפליקציה שלנו מכילה פריט מדיה. אפשר למפות בקלות mediaJSON ל-MediaInfo. כבר ביצענו את שלב 2 בקטע הקודם. קל לבצע את שלב 3 באמצעות Cast SDK.

באפליקציית הדוגמה CastPlayer כבר יש הבחנה בין הפעלה מקומית להפעלה מרחוק בשיטה switchPlayer:

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

ב-Codelab הזה לא חשוב שתבינו בדיוק איך פועלת כל הלוגיקה של השחקן לדוגמה. עם זאת, חשוב להבין שיהיה צורך לשנות את נגן המדיה של האפליקציה כדי שיוכל להפעיל תוכן מקומי וגם תוכן מרחוק.

בשלב הזה, הנגן המקומי תמיד נמצא במצב הפעלה מקומי, כי הוא עדיין לא יודע דבר על מצבי ההעברה. אנחנו צריכים לעדכן את ממשק המשתמש על סמך מעברי מצב שמתרחשים ב-Cast Framework. לדוגמה, אם מתחילים להפעיל Cast, צריך להפסיק את ההפעלה המקומית ולהשבית חלק מהאמצעים לשליטה בהפעלה. באופן דומה, אם נפסיק את ההפעלה ל-Chromecast כשאנחנו בבקר התצוגה הזה, נצטרך לעבור להפעלה מקומית. כדי לטפל בזה, צריך להאזין לאירועים השונים שנוצרים על ידי Cast Framework.

ניהול סשנים של Cast

במסגרת Cast, סשן Cast משלב את השלבים של התחברות למכשיר, הפעלה (או הצטרפות לסשן קיים), התחברות לאפליקציית מקלט ואתחול של ערוץ בקרת מדיה, אם צריך. ערוץ בקרת המדיה הוא הדרך שבה מסגרת Cast שולחת ומקבלת הודעות שקשורות להפעלת מדיה מהמקלט.

הפעלת Cast תתחיל אוטומטית כשהמשתמש יבחר מכשיר מהכפתור להפעלת Cast, ותיפסק אוטומטית כשהמשתמש ינתק את החיבור. גם חיבור מחדש לסשן של מקלט בגלל בעיות ברשת מתבצע אוטומטית על ידי Cast Framework.

הפעילות של Cast מנוהלת על ידי CastSession, שאפשר לגשת אליה דרך cast.framework.CastContext.getInstance().getCurrentSession(). אפשר להשתמש בEventListener callbacks כדי לעקוב אחרי אירועים בסשן, כמו יצירה, השעיה, חידוש וסיום.

בבקשה הנוכחית שלנו, כל הניהול של הסשן והמצב מתבצע בשבילנו בשיטה 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();
};

עדיין צריך לקשר את כל האירועים מ-callbacks ולטפל בכל האירועים שמתקבלים. הפעולה הזו פשוטה יחסית, אז בואו נבצע אותה עכשיו:

/**
 * 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 SDK, המחלקות RemotePlayer ו- RemotePlayerController מספקות קבוצה של ממשקי API נוחים לניהול ההפעלה של מדיה מרחוק במקלט. ב-CastSession שתומך בהפעלת מדיה, מופעים של RemotePlayer ושל RemotePlayerController ייווצרו באופן אוטומטי על ידי ה-SDK. אפשר לגשת אליהם על ידי יצירת מופעים של cast.framework.RemotePlayer ושל cast.framework.RemotePlayerController בהתאמה, כמו שמוצג בהמשך ב-Codelab.

לאחר מכן, צריך לטעון את הסרטון שנבחר כרגע במכשיר המקבל על ידי יצירת אובייקט 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);

    //...
};

עכשיו מוסיפים method למעבר בין הפעלה מקומית להפעלה מרחוק:

/**
 * 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 : '');
  }
};

עכשיו מפעילים את האפליקציה, מתחברים למכשיר Cast ומתחילים להפעיל סרטון. הסרטון אמור להתחיל לפעול במכשיר המקבל.

7. הוספת תמיכה ב-Cast Connect

ספריית Cast Connect מאפשרת לאפליקציות שולחות קיימות לתקשר עם אפליקציות Android TV באמצעות פרוטוקול Cast. ‫Cast Connect מבוסס על תשתית Cast, ואפליקציה ל-Android TV פועלת כמקלט.

תלויות

  • דפדפן Chrome מגרסה M87 ואילך

הגדרת תאימות של מקלט Android

כדי להפעיל את אפליקציית Android TV, שנקראת גם Android Receiver, צריך להגדיר את הדגל 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);
...

הגדרת פרטי כניסה בבקשת טעינה

אם אפליקציית Web Receiver ואפליקציית 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';
...

בהתאם לאפליקציית המקלט שאליה המכשיר השולח מבצע Cast, ה-SDK יטפל עכשיו באופן אוטומטי בפרטי הכניסה שבהם צריך להשתמש בסשן הנוכחי.

בדיקת Cast Connect

כך מתקינים את קובץ ה-APK של Android TV ב-Chromecast with Google TV:

  1. מאתרים את כתובת ה-IP של מכשיר Android TV. בדרך כלל, הוא מופיע בקטע הגדרות > רשת ואינטרנט > (שם הרשת שאליה המכשיר מחובר). בצד שמאל יופיעו הפרטים וכתובת ה-IP של המכשיר ברשת.
  2. משתמשים בכתובת ה-IP של המכשיר כדי להתחבר אליו באמצעות ADB דרך הטרמינל:
$ adb connect <device_ip_address>:5555
  1. בחלון הטרמינל, עוברים לתיקייה ברמה העליונה של הדוגמאות של ה-codelab שהורדתם בתחילת ה-codelab הזה. לדוגמה:
$ cd Desktop/chrome_codelab_src
  1. כדי להתקין את קובץ ה-APK בתיקייה הזו ב-Android TV, מריצים את הפקודה:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. עכשיו אמורה להופיע אפליקציה בשם Cast Videos בתפריט Your Apps במכשיר Android TV.
  2. מריצים את קוד השולח המעודכן באינטרנט ויוצרים סשן Cast עם מכשיר Android TV באמצעות סמל ה-Cast או על ידי בחירה באפשרות Cast.. בתפריט הנפתח בדפדפן Chrome. האפליקציה ל-Android TV אמורה להיפתח בממיר Android, ותהיה לכם אפשרות לשלוט בהפעלת התוכן באמצעות השלט הרחוק של Android TV.

8. מזל טוב

עכשיו אתם יודעים איך להוסיף תמיכה ב-Cast לאפליקציית וידאו באמצעות הווידג'טים של Cast SDK באפליקציית אינטרנט ב-Chrome.

פרטים נוספים זמינים במדריך למפתחים בנושא שליחה באינטרנט.