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

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

הלוגו של Google Cast

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

מה זה Google Cast?

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

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

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

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

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

מה תלמדו

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

מה הדרישות כדי להצטרף לתוכנית?

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

ניסיון

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

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

קריאה בלבד קריאה והשלמה של התרגילים

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

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

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

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

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

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

ופורקים את קובץ ה-ZIP שהורד.

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

הלוגו של Google Chrome

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

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

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

הפעלת השרת

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

index.html

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

יש כמה קטעי צפיות, לנו div#main_video, שמכיל את רכיב הווידאו. שקשור ל-div של הווידאו, יש לנו את div#media_control, שמגדיר את כל הפקדים של רכיב הווידאו. מתחתיו מופיע media_info, שבו פרטי הסרטון מוצגים. לבסוף, ה-div carousel מציג רשימה של סרטונים ב-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 או להתנתק ממנו בכל מסך באפליקציה שלך, כפי שמתואר ברשימת המשימות לעיצוב Google Cast.

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

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

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

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

אתחול

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

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

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

מוסיפים את הקוד הבא אל 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;
  //...
};

לחצן הפעלת Cast

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

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

ניהול פעילות CAST

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

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

סשנים מנוהלים על ידי CastSession, שאליו ניתן לגשת דרך cast.framework.CastContext.getInstance().getCurrentSession(). ניתן להשתמש בקריאות החוזרות (callback) של 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 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);

    //...
};

עכשיו צריך להוסיף שיטה למעבר בין הפעלה מקומית להפעלה מרחוק:

/**
 * 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 מתבססת על תשתית ההעברה, ואפליקציית 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';
...

בהתאם לאפליקציית הנמען שאליה השולח מעביר, ערכת ה-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. מחלון ה-Terminal, עוברים לתיקייה ברמה העליונה של דוגמאות Codelab שהורדתם בתחילת ה-Codelab הזה. לדוגמה:
$ cd Desktop/chrome_codelab_src
  1. מתקינים את קובץ ה- .apk בתיקייה הזו ב-Android TV על ידי הפעלת:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. עכשיו אמורה להופיע אפליקציה בשם העברת סרטונים בתפריט האפליקציות שלך במכשיר Android TV.
  2. מריצים את הקוד המעודכן של שולח האינטרנט ויוצרים סשן העברה (cast) במכשיר ה-Android TV באמצעות סמל ההעברה, או בוחרים באפשרות Cast.. בתפריט הנפתח בדפדפן Chrome. פעולה זו אמורה להפעיל את האפליקציה Android TV ב-Android Receiver ולאפשר לך לשלוט בהפעלה באמצעות השלט הרחוק של Android TV.

8. מזל טוב

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

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