Cast in Android-App integrieren

In diesem Entwicklerleitfaden wird beschrieben, wie Sie die Google Cast-Unterstützung auf Ihrem Android-Gerät einrichten. die das Android Sender SDK verwendet.

Das Mobilgerät oder der Laptop ist der Sender, der die Wiedergabe steuert. Das Google Cast-Gerät ist der Receiver, mit dem die Inhalte auf dem Fernseher wiedergegeben werden.

Das Absender-Framework bezieht sich auf das Binärprogramm der Cast-Klassenbibliothek und die zugehörigen Ressourcen, die zur Laufzeit auf dem Absender vorhanden sind. Die Absender-App oder die Cast App bezieht sich auf eine App, die auch auf dem Absender ausgeführt wird. Web Receiver App bezieht sich auf die HTML-App, die auf dem für Google Cast optimierten Gerät ausgeführt wird.

Das Absender-Framework nutzt ein asynchrones Callback-Design, um den Absender zu informieren. App für Ereignisse und den Wechsel zwischen verschiedenen Stadien der Cast App. Zyklus.

Anwendungsfluss

In den folgenden Schritten wird der typische allgemeine Ausführungsablauf für einen Absender beschrieben Android-App:

  • Das Cast-Framework startet automatisch MediaRouter Geräteerkennung basierend auf dem Activity-Lebenszyklus
  • Wenn der Nutzer auf das Cast-Symbol klickt, zeigt das Framework das Cast-Symbol mit der Liste der gefundenen Übertragungsgeräte.
  • Wenn der Nutzer ein Übertragungsgerät auswählt, versucht das Framework, die Web Receiver auf dem Übertragungsgerät.
  • Das Framework ruft Callbacks in der Absender-App auf, um zu bestätigen, dass das Web Receiver-App wurde gestartet.
  • Das Framework schafft einen Kommunikationskanal zwischen dem Absender und dem Web. Receiver-Apps.
  • Das Framework nutzt den Kommunikationskanal, um Medien zu laden und zu steuern auf dem Web Receiver wiedergegeben werden.
  • Das Framework synchronisiert den Status der Medienwiedergabe zwischen Absender und Web Receiver: Wenn der Nutzer UI-Aktionen für Absender durchführt, wird das Framework übergeben. Mediensteuerungsanfragen an den Web Receiver gesendet werden, Medienstatusaktualisierungen sendet, aktualisiert das Framework den Status der Absender-UI.
  • Wenn der Nutzer auf das Cast-Symbol klickt, um die Verbindung zum Übertragungsgerät zu trennen, trennt das Framework die Absender-App vom Web Receiver.

Für eine umfassende Liste aller Klassen, Methoden und Ereignisse in Google Cast Android SDK finden Sie in der Referenz zur Google Cast Sender API für Android-Geräte In den folgenden Abschnitten erfahren Sie, wie Sie Google Cast zu Ihrer Android-App hinzufügen.

Android-Manifest konfigurieren

Für die Datei AndroidManifest.xml Ihrer App müssen Sie Folgendes konfigurieren: Elemente für das Cast SDK:

uses-sdk

Lege die Mindest- und Ziel-API-Levels für Android fest, die vom Cast SDK unterstützt werden. Derzeit ist das Minimum das API-Level 23 und das Ziel ist API-Level 34.

<uses-sdk
        android:minSdkVersion="23"
        android:targetSdkVersion="34" />

android:theme

Legen Sie das Design Ihrer App basierend auf der Android SDK-Mindestversion fest. Wenn beispielsweise Sie kein eigenes Theme implementieren, sollten Sie eine Variante von Theme.AppCompat beim Targeting auf eine Android SDK-Mindestversion, die vor Lollipop.

<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat" >
       ...
</application>

Cast-Kontext initialisieren

Das Framework hat ein globales Singleton-Objekt, das CastContext, das Koordinaten alle Interaktionen des Frameworks.

Ihre App muss die OptionsProvider mit den Optionen zum Initialisieren der CastContext Singleton. OptionsProvider stellt eine Instanz von CastOptions mit Optionen, die sich auf das Verhalten des Frameworks auswirken. Die meisten wichtig ist die Web Receiver-Anwendungs-ID, die zum Filtern und die Web Receiver App zu starten, wenn ein Stream läuft begonnen.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
class CastOptionsProvider : OptionsProvider {
    override fun getCastOptions(context: Context): CastOptions {
        return Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}
Java
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
        CastOptions castOptions = new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .build();
        return castOptions;
    }
    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Du musst den voll qualifizierten Namen der implementierten OptionsProvider angeben als Metadatenfeld in der Datei AndroidManifest.xml der Sender-App:

<application>
    ...
    <meta-data
        android:name=
            "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
        android:value="com.foo.CastOptionsProvider" />
</application>

CastContext wird verzögert initialisiert, wenn CastContext.getSharedInstance() aufgerufen wird.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
class MyActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val castContext = CastContext.getSharedInstance(this)
    }
}
Java
public class MyActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        CastContext castContext = CastContext.getSharedInstance(this);
    }
}

Die Cast UX-Widgets

Das Cast-Framework umfasst die Widgets, die dem Cast-Design entsprechen. Checkliste:

  • Einführendes Overlay: Das Framework bietet eine benutzerdefinierte Ansicht, IntroductoryOverlay, der dem Nutzer angezeigt wird, um die Aufmerksamkeit auf das Cast-Symbol zu lenken. wenn ein Receiver zum ersten Mal verfügbar ist. Die Sender-App kann den Text und die Position des Titels anpassen, Text.

  • Cast-Symbol: Das Cast-Symbol ist immer sichtbar, unabhängig davon, ob Übertragungsgeräte verfügbar sind. Wenn der Nutzer zum ersten Mal auf das Cast-Symbol klickt, wird ein Cast-Dialogfeld angezeigt. der die gefundenen Geräte enthält. Wenn der Nutzer auf das Cast-Symbol klickt Solange das Gerät verbunden ist, werden die aktuellen Medienmetadaten (z. B. Titel, den Namen des Tonstudios und eine Miniaturansicht) um die Verbindung zum Übertragungsgerät zu trennen. Das Cast-Symbol wird manchmal auch als als „Cast-Symbol“.

  • Mini-Controller: Wenn der Nutzer Inhalte streamt und die aktuelle Seite verlassen hat oder den Controller auf einen anderen Bildschirm in der Absender-App erweitert haben, wird unten auf dem Bildschirm angezeigt, die gerade gestreamten Medienmetadaten abrufen und die Wiedergabe steuern können.

  • Erweiterter Controller: Wenn der Nutzer Inhalte streamt und auf die Medienbenachrichtigung klickt oder Mini-Controller erscheint, wird der maximierte Controller gestartet, der die die gerade Medienmetadaten wiedergeben, und bietet mehrere Schaltflächen zum der Medienwiedergabe.

  • Benachrichtigung: Nur für Android. Wenn der Nutzer Inhalte streamt und das Fenster verlässt, Absender-App angezeigt wird, wird eine Medienbenachrichtigung mit der aktuellen Übertragung Medienmetadaten und Wiedergabesteuerung.

  • Sperrbildschirm: Nur für Android. Wenn der Nutzer Inhalte streamt und navigiert (oder das Gerät navigiert) Zeitüberschreitung) zum Sperrbildschirm erscheint, wird ein Steuerelement für den zeigt die aktuell gestreamten Medienmetadaten und die Wiedergabesteuerung.

In der folgenden Anleitung wird beschrieben, wie Sie diese Widgets Ihrem für Ihre App.

Cast-Symbol hinzufügen

Das Android- MediaRouter APIs wurden entwickelt, um die Medienanzeige und -wiedergabe auf sekundären Geräten zu ermöglichen. Android-Apps, die die MediaRouter API verwenden, sollten ein Cast-Symbol als Teil des Tools enthalten ihrer Benutzeroberfläche, um ihnen zu ermöglichen, eine Medienroute auszuwählen, auf der Medien wiedergegeben werden sollen. einem sekundären Gerät, z. B. einem Übertragungsgerät.

Das Framework macht das Hinzufügen eines MediaRouteButton als Cast button sehr einfach. Du musst zuerst einen Menüpunkt oder ein MediaRouteButton in der XML-Datei hinzufügen die Ihre Speisekarte definiert, und verwenden CastButtonFactory um sie mit dem Framework zu verbinden.

// To add a Cast button, add the following snippet.
// menu.xml
<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always" />
<ph type="x-smartling-placeholder">
</ph>
Kotlin
// Then override the onCreateOptionMenu() for each of your activities.
// MyActivity.kt
override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)
    menuInflater.inflate(R.menu.main, menu)
    CastButtonFactory.setUpMediaRouteButton(
        applicationContext,
        menu,
        R.id.media_route_menu_item
    )
    return true
}
Java
// Then override the onCreateOptionMenu() for each of your activities.
// MyActivity.java
@Override public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.main, menu);
    CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                                            menu,
                                            R.id.media_route_menu_item);
    return true;
}

Wenn Activity dann übernimmt von FragmentActivity können Sie eine MediaRouteButton zu Ihrem Layout hinzufügen.

// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:gravity="center_vertical"
   android:orientation="horizontal" >

   <androidx.mediarouter.app.MediaRouteButton
       android:id="@+id/media_route_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_weight="1"
       android:mediaRouteTypes="user"
       android:visibility="gone" />

</LinearLayout>
<ph type="x-smartling-placeholder">
</ph>
Kotlin
// MyActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_layout)

    mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton
    CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton)

    mCastContext = CastContext.getSharedInstance(this)
}
Java
// MyActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_layout);

   mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
   CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton);

   mCastContext = CastContext.getSharedInstance(this);
}

Wie Sie die Darstellung des Cast-Symbols mithilfe eines Designs festlegen, erfahren Sie unter Cast-Symbol anpassen

Geräteerkennung konfigurieren

Die Geräteerkennung wird vollständig vom CastContext Bei der Initialisierung von CastContext gibt die Absender-App den Web Receiver an Anwendungs-ID und kann optional Namespace-Filterung anfordern, indem Sie Folgendes festlegen: supportedNamespaces Zoll CastOptions CastContext enthält intern einen Verweis auf die MediaRouter und wird gestartet. unter folgenden Bedingungen ausführen:

  • Basiert auf einem Algorithmus, der die Latenz bei der Geräteerkennung und Akkunutzung ist, wird die Erkennung gelegentlich automatisch gestartet, geht die Absender-App in den Vordergrund.
  • Das Dialogfeld „Streamen“ ist geöffnet.
  • Das Cast SDK versucht, eine Cast-Sitzung wiederherzustellen.

Die Erkennung wird angehalten, wenn das Cast-Dialogfeld geschlossen oder die Absender-App in den Hintergrund wechselt.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
class CastOptionsProvider : OptionsProvider {
    companion object {
        const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"
    }

    override fun getCastOptions(appContext: Context): CastOptions {
        val supportedNamespaces: MutableList<String> = ArrayList()
        supportedNamespaces.add(CUSTOM_NAMESPACE)

        return CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setSupportedNamespaces(supportedNamespaces)
            .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}
Java
class CastOptionsProvider implements OptionsProvider {
    public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace";

    @Override
    public CastOptions getCastOptions(Context appContext) {
        List<String> supportedNamespaces = new ArrayList<>();
        supportedNamespaces.add(CUSTOM_NAMESPACE);

        CastOptions castOptions = new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setSupportedNamespaces(supportedNamespaces)
            .build();
        return castOptions;
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Funktionsweise der Sitzungsverwaltung

Mit dem Cast SDK wird das Konzept einer Cast-Sitzung eingeführt. Einrichtung, die die Schritte zum Herstellen einer Verbindung mit einem Gerät, dem Starten (oder dem Beitritt) zu einem Web umfasst Receiver-App, die eine Verbindung zu dieser App herstellt und ein Mediensteuerungskanal initialisiert wird. Web Receiver anzeigen Leitfaden zum Lebenszyklus von Anwendungen finden Sie weitere Informationen zu Übertragungssitzungen und zum Lebenszyklus des Web-Receivers.

Sitzungen werden vom Kurs verwaltet SessionManager, auf die Ihre App über CastContext.getSessionManager() Einzelne Sitzungen werden durch Unterklassen der Klasse dargestellt. Session Beispiel: CastSession Sitzungen mit Übertragungsgeräten. Ihre App kann auf die derzeit aktiven Sitzung streamen über SessionManager.getCurrentCastSession()

Deine App kann die SessionManagerListener zur Überwachung von Sitzungsereignissen wie Erstellung, Sperrung, Wiederaufnahme und Kündigung. Das Framework versucht automatisch, abnormale/abrupte Beendigung während einer aktiven Sitzung.

Sitzungen werden als Reaktion auf Nutzergesten automatisch erstellt und gelöscht. aus den MediaRouter-Dialogfeldern.

Für ein besseres Verständnis von Übertragungsfehlern können Apps Folgendes verwenden: CastContext#getCastReasonCodeForCastStatusCode(int) zum Konvertieren des Sitzungsstartfehlers in CastReasonCodes Hinweis: Einige Fehler beim Starten der Sitzung (z.B. CastReasonCodes#CAST_CANCELLED) sind beabsichtigt und sollten nicht als Fehler protokolliert werden.

Wenn Sie die Statusänderungen für die Sitzung kennen müssen, können Sie SessionManagerListener. In diesem Beispiel wird die Verfügbarkeit eines CastSession in Activity.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
class MyActivity : Activity() {
    private var mCastSession: CastSession? = null
    private lateinit var mCastContext: CastContext
    private lateinit var mSessionManager: SessionManager
    private val mSessionManagerListener: SessionManagerListener<CastSession> =
        SessionManagerListenerImpl()

    private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> {
        override fun onSessionStarting(session: CastSession?) {}

        override fun onSessionStarted(session: CastSession?, sessionId: String) {
            invalidateOptionsMenu()
        }

        override fun onSessionStartFailed(session: CastSession?, error: Int) {
            val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error)
            // Handle error
        }

        override fun onSessionSuspended(session: CastSession?, reason Int) {}

        override fun onSessionResuming(session: CastSession?, sessionId: String) {}

        override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) {
            invalidateOptionsMenu()
        }

        override fun onSessionResumeFailed(session: CastSession?, error: Int) {}

        override fun onSessionEnding(session: CastSession?) {}

        override fun onSessionEnded(session: CastSession?, error: Int) {
            finish()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mCastContext = CastContext.getSharedInstance(this)
        mSessionManager = mCastContext.sessionManager
    }

    override fun onResume() {
        super.onResume()
        mCastSession = mSessionManager.currentCastSession
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }

    override fun onPause() {
        super.onPause()
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java)
        mCastSession = null
    }
}
Java
public class MyActivity extends Activity {
    private CastContext mCastContext;
    private CastSession mCastSession;
    private SessionManager mSessionManager;
    private SessionManagerListener<CastSession> mSessionManagerListener =
            new SessionManagerListenerImpl();

    private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> {
        @Override
        public void onSessionStarting(CastSession session) {}
        @Override
        public void onSessionStarted(CastSession session, String sessionId) {
            invalidateOptionsMenu();
        }
        @Override
        public void onSessionStartFailed(CastSession session, int error) {
            int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error);
            // Handle error
        }
        @Override
        public void onSessionSuspended(CastSession session, int reason) {}
        @Override
        public void onSessionResuming(CastSession session, String sessionId) {}
        @Override
        public void onSessionResumed(CastSession session, boolean wasSuspended) {
            invalidateOptionsMenu();
        }
        @Override
        public void onSessionResumeFailed(CastSession session, int error) {}
        @Override
        public void onSessionEnding(CastSession session) {}
        @Override
        public void onSessionEnded(CastSession session, int error) {
            finish();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCastContext = CastContext.getSharedInstance(this);
        mSessionManager = mCastContext.getSessionManager();
    }
    @Override
    protected void onResume() {
        super.onResume();
        mCastSession = mSessionManager.getCurrentCastSession();
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class);
    }
    @Override
    protected void onPause() {
        super.onPause();
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class);
        mCastSession = null;
    }
}

Stream-Übertragung

Die Beibehaltung des Sitzungsstatus ist die Grundlage der Streamübertragung. können Nutzer vorhandene Audio- und Videostreams per Sprachbefehl, über Google Home eine App oder ein Smart Display verwenden. Die Medienwiedergabe wird auf einem Gerät (Quelle) gestoppt und auf einem anderen fortgesetzt (das Ziel). Jedes Übertragungsgerät mit der neuesten Firmware kann als Quelle oder Ziel in einem Streamübertragung.

So rufen Sie das neue Zielgerät während einer Stream-Übertragung oder -Erweiterung ab: registrieren Cast.Listener mithilfe der CastSession#addCastListener Rufen Sie dann CastSession#getCastDevice() während des onDeviceNameChanged-Callbacks.

Weitere Informationen finden Sie unter Stream-Übertragung mit Web Receiver .

Automatische Wiederherstellung der Verbindung

Das Framework bietet eine ReconnectionService die von der Absender-App aktiviert werden kann, um die erneute Verbindung in vielen subtilen Sonderfälle wie:

  • Wiederherstellung nach einem vorübergehenden WLAN-Ausfall
  • Aus Geräte-Ruhemodus wiederherstellen
  • Nach Hintergrundwiedergabe wiederherstellen
  • Wiederherstellung nach einem Absturz der App

Dieser Dienst ist standardmäßig aktiviert und kann in den CastOptions.Builder

Dieser Dienst kann bei der automatischen Zusammenführung automatisch mit dem Manifest Ihrer App zusammengeführt werden ist in Ihrer Gradle-Datei aktiviert.

Das Framework startet den Dienst, wenn eine Mediensitzung stattfindet, und beendet ihn wenn die Mediensitzung endet.

So funktioniert die Mediensteuerung

Durch das Cast-Framework wird die RemoteMediaPlayer von Cast 2.x zugunsten einer neuen Klasse RemoteMediaClient die die gleiche Funktionalität in einer Reihe einfacherer APIs bietet. vermeidet, einen GoogleApiClient übergeben zu müssen.

Wenn Ihre App eine CastSession mit einer Web Receiver-App, die den Media-Namespace unterstützt, eine Instanz von RemoteMediaClient wird vom Framework automatisch erstellt. kann Ihre App Sie können darauf zugreifen, indem Sie die Methode getRemoteMediaClient() auf der CastSession aufrufen. Instanz.

Alle Methoden von RemoteMediaClient, die Anfragen an den Web Receiver senden, gibt ein PendingResult-Objekt zurück, mit dem die Anfrage verfolgt werden kann.

Es wird erwartet, dass die Instanz von RemoteMediaClient von Teile deiner App und einige interne Komponenten des wie die persistenten Mini-Controller und den Benachrichtigungsdienst. Zu diesem Zweck unterstützt diese Instanz die Registrierung mehrerer Instanzen von RemoteMediaClient.Listener

Medienmetadaten festlegen

Die MediaMetadata stellt die Informationen zu einem Medienelement dar, das Sie streamen möchten. Die Im folgenden Beispiel wird eine neue MediaMetadata-Instanz eines Films erstellt und die Titel, Untertitel und zwei Bilder.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)

movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle())
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio())
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0))))
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
Java
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);

movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle());
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio());
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0))));
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));

Weitere Informationen finden Sie unter Bildauswahl zur Verwendung von Bildern mit Medienmetadaten.

Medien laden

Ihre App kann Medienelemente laden, wie im folgenden Code gezeigt. Erste Verwendung MediaInfo.Builder mit den Metadaten der Medien, um ein MediaInfo Instanz. Holen Sie sich RemoteMediaClient aus dem aktuellen CastSession und laden Sie dann MediaInfo in diese RemoteMediaClient. RemoteMediaClient zum Abspielen, Pausieren und sonstigen Aktionen verwenden eine Mediaplayer-App zu steuern, die auf dem Web Receiver ausgeführt wird.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl())
    .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
    .setContentType("videos/mp4")
    .setMetadata(movieMetadata)
    .setStreamDuration(mSelectedMedia.getDuration() * 1000)
    .build()
val remoteMediaClient = mCastSession.getRemoteMediaClient()
remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
Java
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl())
        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
        .setContentType("videos/mp4")
        .setMetadata(movieMetadata)
        .setStreamDuration(mSelectedMedia.getDuration() * 1000)
        .build();
RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());

Weitere Informationen finden Sie im Abschnitt mit Media-Tracks.

4K-Videoformat

Um zu prüfen, welches Videoformat Ihre Medien haben, verwenden Sie getVideoInfo() in MediaStatus ein, um die aktuelle Instanz von VideoInfo Diese Instanz enthält den Typ des HDR-TV-Formats und die Displayhöhe und Breite in Pixeln. Varianten des 4K-Formats werden durch Konstanten angegeben HDR_TYPE_*

Ferngesteuerte Benachrichtigungen an mehrere Geräte

Wenn ein Nutzer Inhalte streamt, werden andere Android-Geräte im selben Netzwerk um auch die Wiedergabe zu steuern. Jeder, dessen Gerät solche Benachrichtigungen erhält, können Sie sie in den Einstellungen für dieses Gerät deaktivieren. App bei Google > Google Cast > Fernbedienungs-Benachrichtigungen anzeigen (Die Benachrichtigungen enthalten eine Verknüpfung zur App „Einstellungen“.) Weitere Informationen finden Sie unter Benachrichtigungen zur Cast-Fernsteuerung

Mini-Controller hinzufügen

Gemäß dem Cast-Design Checkliste, sollte eine Absender-App eine dauerhafte Steuerung bieten, die sogenannte Miniversion. Controller die angezeigt werden sollte, wenn der Nutzer von der aktuellen Content-Seite zu Teil der Absender-App. Über den Mini-Controller wird eine sichtbare Erinnerung angezeigt. für den Nutzer der aktuellen Streaming-Sitzung. Durch Tippen auf den Mini-Controller kann der Nutzer zur erweiterten Controller-Vollbildansicht für Cast zurückkehren.

Das Framework bietet die benutzerdefinierte Ansicht MiniControllerFragment, die Sie unten in die Layoutdatei jeder Aktivität, in der Sie die Mini-Controller.

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />

Wenn die Sender-App einen Video- oder Audio-Livestream wiedergibt, gibt das SDK zeigt automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche an. im Mini-Controller.

Um die Textdarstellung des Titels und Untertitels dieser benutzerdefinierten Ansicht festzulegen, und zur Auswahl von Schaltflächen Mini-Controller anpassen

Maximierten Controller hinzufügen

Die Checkliste für das Google Cast-Design erfordert, dass eine Sender-App eine erweiterte Version Controller für die Medien, die gestreamt werden. Der erweiterte Controller ist eine Vollbildversion von den Mini-Controller.

Das Cast SDK bietet ein Widget für den erweiterten Controller namens ExpandedControllerActivity Dies ist eine abstrakte Klasse, deren Klasse Sie ableiten müssen, um ein Cast-Symbol hinzuzufügen.

Erstellen Sie zuerst eine neue Menüressourcendatei für den erweiterten Controller, damit über das Cast-Symbol:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
            android:id="@+id/media_route_menu_item"
            android:title="@string/media_route_menu_title"
            app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
            app:showAsAction="always"/>

</menu>

Erstellen Sie eine neue Klasse, die ExpandedControllerActivity erweitert.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}
Java
public class ExpandedControlsActivity extends ExpandedControllerActivity {
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.expanded_controller, menu);
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
        return true;
    }
}

Deklariere jetzt deine neue Aktivität im App-Manifest innerhalb des application-Tags:

<application>
...
<activity
        android:name=".expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait"
        android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>
...
</application>

Bearbeiten Sie die CastOptionsProvider und ändern Sie NotificationOptions und CastMediaOptions, um die Zielaktivität auf die neue Aktivität festzulegen:

<ph type="x-smartling-placeholder">
</ph>
Kotlin
override fun getCastOptions(context: Context): CastOptions? {
    val notificationOptions = NotificationOptions.Builder()
        .setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
        .build()
    val mediaOptions = CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
        .build()

    return CastOptions.Builder()
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build()
}
Java
public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = new NotificationOptions.Builder()
            .setTargetActivityClassName(ExpandedControlsActivity.class.getName())
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
            .build();

    return new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build();
}

Aktualisieren Sie die LocalPlayerActivity-Methode loadRemoteMedia, um Ihre neue Aktivität, wenn die Remote-Medien geladen werden:

<ph type="x-smartling-placeholder">
</ph>
Kotlin
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    val remoteMediaClient = mCastSession?.remoteMediaClient ?: return

    remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
        override fun onStatusUpdated() {
            val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
            startActivity(intent)
            remoteMediaClient.unregisterCallback(this)
        }
    })

    remoteMediaClient.load(
        MediaLoadRequestData.Builder()
            .setMediaInfo(mSelectedMedia)
            .setAutoplay(autoPlay)
            .setCurrentTime(position.toLong()).build()
    )
}
Java
private void loadRemoteMedia(int position, boolean autoPlay) {
    if (mCastSession == null) {
        return;
    }
    final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
    if (remoteMediaClient == null) {
        return;
    }
    remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() {
        @Override
        public void onStatusUpdated() {
            Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class);
            startActivity(intent);
            remoteMediaClient.unregisterCallback(this);
        }
    });
    remoteMediaClient.load(new MediaLoadRequestData.Builder()
            .setMediaInfo(mSelectedMedia)
            .setAutoplay(autoPlay)
            .setCurrentTime(position).build());
}

Wenn die Sender-App einen Video- oder Audio-Livestream wiedergibt, gibt das SDK zeigt automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche an. im maximierten Controller.

Um die Darstellung mithilfe von Designs festzulegen, wähle die anzuzeigenden Schaltflächen aus. und benutzerdefinierte Schaltflächen hinzufügen, Erweiterten Controller anpassen:

Lautstärkeregelung

Das Framework verwaltet automatisch das Volume für die Absender-App. Das Framework synchronisiert automatisch die Sender- und Web Receiver-Apps, Die Benutzeroberfläche meldet immer die vom Web Receiver angegebene Lautstärke.

Lautstärkeregelung mit physischer Taste

Unter Android können die physischen Tasten am Gerät des Senders verwendet werden, um die Lautstärke der Übertragungssitzung auf dem Web Receiver standardmäßig für alle Geräte mit Jelly Bean oder neuer.

Lautstärkeregelung mit physischer Taste vor Jelly Bean

So regeln Sie die Lautstärke des Web Receiver-Geräts mit den physischen Lautstärketasten: Bei Android-Geräten, die älter als Jelly Bean sind, sollte die Sender-App dispatchKeyEvent in ihren Aktivitäten und rufen Sie CastContext.onDispatchVolumeKeyEventBeforeJellyBean():

<ph type="x-smartling-placeholder">
</ph>
Kotlin
class MyActivity : FragmentActivity() {
    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        return (CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
                || super.dispatchKeyEvent(event))
    }
}
Java
class MyActivity extends FragmentActivity {
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        return CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
            || super.dispatchKeyEvent(event);
    }
}

Mediensteuerelemente in Benachrichtigungen und Sperrbildschirm hinzufügen

Nur auf Android-Geräten erfordert die Google Cast-Design-Checkliste eine Sender-App, Mediensteuerung in einem Benachrichtigung und im Schloss Bildschirm, an die der Absender etwas überträgt, die Absender-App aber nicht im Fokus ist. Die Framework bietet MediaNotificationService und MediaIntentReceiver damit die Absender-App Mediensteuerelemente in einer Benachrichtigung und im Schloss erstellen kann Bildschirm.

MediaNotificationService wird ausgeführt, wenn der Absender Inhalte streamt, und zeigt eine Benachrichtigung mit Miniaturansicht und Informationen zum aktuellen Streaming sowie eine Schaltfläche für Wiedergabe/Pause und eine Stopp-Schaltfläche.

MediaIntentReceiver ist eine BroadcastReceiver, die Nutzeraktionen aus die Benachrichtigung.

Deine App kann die Benachrichtigungs- und Mediensteuerung vom Sperrbildschirm bis zum NotificationOptions Ihre App kann festlegen, welche Steuerschaltflächen in der Benachrichtigung angezeigt werden, und Das Activity, das geöffnet werden soll, wenn der Nutzer auf die Benachrichtigung tippt. Wenn-Aktionen nicht explizit angegeben sind, werden die Standardwerte, MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK und MediaIntentReceiver.ACTION_STOP_CASTING wird verwendet.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting".
val buttonActions: MutableList<String> = ArrayList()
buttonActions.add(MediaIntentReceiver.ACTION_REWIND)
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK)
buttonActions.add(MediaIntentReceiver.ACTION_FORWARD)
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING)

// Showing "play/pause" and "stop casting" in the compat view of the notification.
val compatButtonActionsIndices = intArrayOf(1, 3)

// Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds.
// Tapping on the notification opens an Activity with class VideoBrowserActivity.
val notificationOptions = NotificationOptions.Builder()
    .setActions(buttonActions, compatButtonActionsIndices)
    .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
    .setTargetActivityClassName(VideoBrowserActivity::class.java.name)
    .build()
Java
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting".
List<String> buttonActions = new ArrayList<>();
buttonActions.add(MediaIntentReceiver.ACTION_REWIND);
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK);
buttonActions.add(MediaIntentReceiver.ACTION_FORWARD);
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING);

// Showing "play/pause" and "stop casting" in the compat view of the notification.
int[] compatButtonActionsIndices = new int[]{1, 3};

// Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds.
// Tapping on the notification opens an Activity with class VideoBrowserActivity.
NotificationOptions notificationOptions = new NotificationOptions.Builder()
    .setActions(buttonActions, compatButtonActionsIndices)
    .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
    .setTargetActivityClassName(VideoBrowserActivity.class.getName())
    .build();

Die Anzeige der Mediensteuerung für Benachrichtigungen und Sperrbildschirm ist aktiviert von und kann durch Aufrufen von setNotificationOptions mit Null in CastMediaOptions.Builder Derzeit ist der Sperrbildschirm aktiviert, solange die Benachrichtigungsfunktion aktiviert ist. aktiviert ist.

<ph type="x-smartling-placeholder">
</ph>
Kotlin
// ... continue with the NotificationOptions built above
val mediaOptions = CastMediaOptions.Builder()
    .setNotificationOptions(notificationOptions)
    .build()
val castOptions: CastOptions = Builder()
    .setReceiverApplicationId(context.getString(R.string.app_id))
    .setCastMediaOptions(mediaOptions)
    .build()
Java
// ... continue with the NotificationOptions built above
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .build();
CastOptions castOptions = new CastOptions.Builder()
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build();

Wenn die Sender-App einen Video- oder Audio-Livestream wiedergibt, gibt das SDK zeigt automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche an. auf dem Benachrichtigungssteuerelement, aber nicht auf dem Sperrbildschirm.

Hinweis: Auf Geräten mit älteren Lollipop-Versionen werden die Steuerelemente für den Sperrbildschirm angezeigt: RemoteMediaClient fordert automatisch den Audiofokus in deinem Namen an.

Fehler verarbeiten

Absender-Apps müssen alle Fehlerrückrufe verarbeiten und entscheiden, die beste Reaktion für jede Phase des Cast-Lebenszyklus. Die App kann Folgendes anzeigen: wird eine Fehlermeldung angezeigt oder die Verbindung zum Web Receiver.