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

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

הלוגו של Google Cast

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

מה זה Google Cast?

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

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

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

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

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

מה תלמדו

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

מה נדרש

  • דפדפן Google Chrome העדכני ביותר.
  • שירות אירוח HTTPS, כמו אירוח Firebase או ngrok.
  • מכשיר Google 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 ובוחרים את מכשיר ה-Google Cast.
  3. בוחרים סרטון ולוחצים על לחצן ההפעלה.
  4. הסרטון יתחיל לפעול במכשיר Google Cast.

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

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

כדי להפסיק את ההעברה למכשיר Google Cast, לוחצים על הלחצן להפעלת 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, שמכיל את רכיב הווידאו. בקשר ל-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 יש אובייקט גלובלי מסוג סינגלטון, CastContext, שמרכז את כל הפעילויות של ה-framework. חובה לאתחל את האובייקט הזה בשלב מוקדם במחזור החיים של האפליקציה. בדרך כלל הפעולה הזו נקראת קריאה חוזרת (callback) שהוקצתה ל-window['__onGCastApiAvailable'], והיא מופעלת אחרי טעינת ה-Cast SDK וזמינה לשימוש. במקרה הזה, מתבצעת קריאה של CastContext ב-CastPlayer.prototype.initializeCastPlayer, והיא מתבצעת מהקריאה החוזרת שהוזכרה למעלה.

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

ניהול הפעלת Cast

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

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

סשנים של הפעלות Cast מנוהלים על ידי 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();
};

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

/**
 * 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 מבוסס על התשתית של Cast, כשאפליקציית Android TV פועלת כמקלט.

יחסי תלות

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

הגדרת תאימות למקלט של 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 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 של סרטונים בתפריט האפליקציות שלך במכשיר Android TV.
  2. מריצים את הקוד המעודכן של שולח האינטרנט ויוצרים סשן Cast במכשיר ה-Android TV באמצעות הסמל של הפעלת Cast או בוחרים באפשרות Cast.. מהתפריט הנפתח בדפדפן Chrome. פעולה זו אמורה להפעיל את האפליקציה ל-Android TV במקלט ה-Android ולאפשר לך לשלוט בהפעלה באמצעות השלט הרחוק של Android TV.

8. מזל טוב

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

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