Cast in Android-App integrieren

In diesem Entwicklerleitfaden wird beschrieben, wie Sie Ihrer Android Sender-App mit dem Android Sender SDK Google Cast-Unterstützung hinzufügen.

Das Mobilgerät oder der Laptop ist der Sender , der die Wiedergabe steuert, und das Google Cast-Gerät ist der Receiver , der die Inhalte auf dem Fernseher anzeigt.

Das Sender-Framework bezieht sich auf die binäre Cast-Klassenbibliothek und die zugehörigen Ressourcen, die zur Laufzeit auf dem Sender vorhanden sind. Die Sender-App oder Cast-App bezieht sich auf eine App, die ebenfalls auf dem Sender ausgeführt wird. Die Web Receiver App bezieht sich auf die HTML-Anwendung, die auf dem für Cast optimierten Gerät ausgeführt wird.

Das Sender-Framework verwendet ein asynchrones Callback-Design, um die Sender-App über Ereignisse zu informieren und zwischen verschiedenen Zuständen des Lebenszyklus der Cast-App zu wechseln.

Anwendungsfluss

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

  • Das Cast-Framework startet die MediaRouter Geräteerkennung automatisch basierend auf dem Activity Lebenszyklus.
  • Wenn der Nutzer auf die Cast-Schaltfläche klickt, wird im Framework das Cast-Dialogfeld mit der Liste der erkannten Cast-Geräte angezeigt.
  • Wenn der Nutzer ein Cast-Gerät auswählt, versucht das Framework, die Web Receiver App auf dem Cast-Gerät zu starten.
  • Das Framework ruft in der Sender-App Callbacks auf, um zu bestätigen, dass die Web Receiver App gestartet wurde.
  • Das Framework erstellt einen Kommunikationskanal zwischen der Sender- und der Web Receiver App.
  • Das Framework verwendet den Kommunikationskanal, um die Medienwiedergabe auf dem Web Receiver zu laden und zu steuern.
  • Das Framework synchronisiert den Status der Medienwiedergabe zwischen Sender und Web Receiver. Wenn der Nutzer Aktionen auf der Benutzeroberfläche des Senders ausführt, leitet das Framework diese Anfragen zur Mediensteuerung an den Web Receiver weiter. Wenn der Web Receiver Statusaktualisierungen für Medien sendet, aktualisiert das Framework den Status der Benutzeroberfläche des Senders.
  • Wenn der Nutzer auf die Cast-Schaltfläche klickt, um die Verbindung zum Cast-Gerät zu trennen, trennt das Framework die Sender-App vom Web Receiver.

Eine umfassende Liste aller Klassen, Methoden und Ereignisse im Google Cast Android SDK finden Sie in der Google Cast Sender API-Referenz für Android. In den folgenden Abschnitten werden die Schritte beschrieben, mit denen Sie Ihrer Android-App Cast hinzufügen können.

Android-Manifest konfigurieren

In der Datei AndroidManifest.xml Ihrer App müssen Sie die folgenden Elemente für das Cast SDK konfigurieren:

uses-sdk

Legen Sie die minimalen und Ziel-Android-API-Levels fest, die vom Cast SDK unterstützt werden. Derzeit ist das Minimum API-Level 23 und das Ziel API-Level 34.

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

android:theme

Legen Sie das Design Ihrer App basierend auf der minimalen Android SDK-Version fest. Wenn Sie beispielsweise kein eigenes Design implementieren, sollten Sie eine Variante von Theme.AppCompat verwenden, wenn Sie auf eine minimale Android SDK-Version vor Lollipop abzielen.

<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, CastContext, das alle Interaktionen des Frameworks koordiniert.

Ihre App muss die OptionsProvider Schnittstelle implementieren, um Optionen bereitzustellen, die zum Initialisieren des CastContext Singleton erforderlich sind. OptionsProvider stellt eine Instanz von CastOptions bereit, die Optionen enthält, die sich auf das Verhalten des Frameworks auswirken. Die wichtigste davon ist die Web Receiver-Anwendungs-ID, die verwendet wird, um Erkennungsergebnisse zu filtern und die Web Receiver App zu starten, wenn eine Cast-Sitzung gestartet wird.

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;
    }
}

Sie müssen den vollständig qualifizierten Namen des implementierten OptionsProvider als Metadatenfeld in der Datei AndroidManifest.xml der Sender-App deklarieren:

<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.

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);
    }
}

Cast-UX-Widgets

Das Cast-Framework bietet die Widgets, die der Cast-Design-Checkliste entsprechen:

  • Einführungs-Overlay: Das Framework bietet eine benutzerdefinierte Ansicht, IntroductoryOverlay, die dem Nutzer angezeigt wird, um ihn auf die Cast-Schaltfläche aufmerksam zu machen, wenn zum ersten Mal ein Receiver verfügbar ist. Die Sender-App kann den Text und die Position des Titels anpassen.

  • Cast-Schaltfläche: Die Cast-Schaltfläche ist unabhängig von der Verfügbarkeit von Cast-Geräten sichtbar. Wenn der Nutzer zum ersten Mal auf die Cast-Schaltfläche klickt, wird ein Cast-Dialogfeld mit den erkannten Geräten angezeigt. Wenn der Nutzer auf die Cast-Schaltfläche klickt, während das Gerät verbunden ist, werden die aktuellen Metadaten der Medien (z. B. Titel, Name des Aufnahmestudios und ein Thumbnail) angezeigt. Der Nutzer kann auch die Verbindung zum Cast-Gerät trennen. Die „Cast-Schaltfläche“ wird manchmal auch als „Cast-Symbol“ bezeichnet.

  • Mini-Controller: Wenn der Nutzer Inhalte streamt und die aktuelle Inhaltsseite oder den erweiterten Controller zu einem anderen Bildschirm in der Sender-App verlassen hat, wird der Mini-Controller unten auf dem Bildschirm angezeigt. So kann der Nutzer die Metadaten der aktuell gestreamten Medien sehen und die Wiedergabe steuern.

  • Erweiterter Controller: Wenn der Nutzer Inhalte streamt und auf die Medienbenachrichtigung oder den Mini-Controller klickt, wird der erweiterte Controller gestartet. Dort werden die Metadaten der aktuell wiedergegebenen Medien angezeigt und es gibt mehrere Schaltflächen zur Steuerung der Medienwiedergabe.

  • Benachrichtigung: Nur Android. Wenn der Nutzer Inhalte streamt und die Sender-App verlässt, wird eine Medienbenachrichtigung mit den Metadaten der aktuell gestreamten Medien und der Wiedergabesteuerung angezeigt.

  • Sperrbildschirm: Nur Android. Wenn der Nutzer Inhalte streamt und zum Sperrbildschirm wechselt (oder das Gerät eine Zeitüberschreitung aufweist), wird ein Mediensteuerelement für den Sperrbildschirm mit den Metadaten der aktuell gestreamten Medien und der Wiedergabesteuerung angezeigt.

Im folgenden Leitfaden wird beschrieben, wie Sie diese Widgets Ihrer App hinzufügen.

Cast-Schaltfläche hinzufügen

Die 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 eine Cast-Schaltfläche als Teil der Benutzeroberfläche enthalten, damit Nutzer eine Medienroute auswählen können, um Medien auf einem sekundären Gerät wie einem Cast-Gerät wiederzugeben.

Das Framework macht das Hinzufügen einer MediaRouteButton als einer Cast button sehr einfach. Fügen Sie zuerst ein Menüelement oder eine MediaRouteButton in der XML Datei hinzu, die Ihr Menü definiert, und verwenden Sie CastButtonFactory , um es mit dem Framework zu verknüpfen.

// 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" />
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 Ihre Activity von FragmentActivityerbt, können Sie Ihrem Layout eine MediaRouteButton 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>
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);
}

Informationen zum Festlegen des Erscheinungsbilds der Cast-Schaltfläche mit einem Design finden Sie unter Cast-Schaltfläche anpassen.

Geräteerkennung konfigurieren

Die Geräteerkennung wird vollständig von der CastContext verwaltet. Beim Initialisieren von CastContext gibt die Sender-App die Web Receiver Anwendungs-ID an und kann optional eine Namespace-Filterung anfordern, indem sie supportedNamespaces in CastOptions festlegt. CastContext enthält intern einen Verweis auf MediaRouter und startet den Erkennungsprozess unter den folgenden Bedingungen:

  • Basierend auf einem Algorithmus, der die Latenz der Geräteerkennung und den Akkuverbrauch ausgleicht, wird die Erkennung gelegentlich automatisch gestartet, wenn die Sender-App in den Vordergrund tritt.
  • Das Cast-Dialogfeld ist geöffnet.
  • Das Cast SDK versucht, eine Cast-Sitzung wiederherzustellen.

Der Erkennungsprozess wird beendet, wenn das Cast-Dialogfeld geschlossen wird oder die Sender-App in den Hintergrund tritt.

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

Im Cast SDK wird das Konzept einer Cast-Sitzung eingeführt. Die Einrichtung einer solchen Sitzung umfasst die Schritte zum Herstellen einer Verbindung zu einem Gerät, zum Starten (oder Beitreten) einer Web Receiver-App, zum Herstellen einer Verbindung zu dieser App und zum Initialisieren eines Mediensteuerung-Channels. Weitere Informationen zu Cast-Sitzungen und zum Web Receiver-Lebenszyklus finden Sie im Leitfaden zum Anwendungslebenszyklus für Web Receiver.

Sitzungen werden von der Klasse SessionManager, verwaltet, auf die Ihre App über CastContext.getSessionManager()zugreifen kann. Einzelne Sitzungen werden durch Unterklassen der Klasse Session dargestellt. Beispielsweise steht CastSession für Sitzungen mit Cast-Geräten. Ihre App kann über SessionManager.getCurrentCastSession() auf die derzeit aktive Cast-Sitzung zugreifen.

Ihre App kann die SessionManagerListener Klasse verwenden, um Sitzungsereignisse wie Erstellung, Unterbrechung, Fortsetzung und Beendigung zu beobachten. Das Framework versucht automatisch, die Sitzung nach einer ungewöhnlichen/plötzlichen Beendigung wiederaufzunehmen, während sie aktiv war.

Sitzungen werden automatisch erstellt und beendet, wenn der Nutzer Aktionen in den MediaRouter-Dialogfeldern ausführt.

Um Fehler beim Starten von Cast besser zu verstehen, können Apps CastContext#getCastReasonCodeForCastStatusCode(int) verwenden, um den Fehler beim Starten der Sitzung in CastReasonCodeszu konvertieren. Einige Fehler beim Starten der Sitzung (z.B. CastReasonCodes#CAST_CANCELLED) sind beabsichtigt und sollten nicht als Fehler protokolliert werden.

Wenn Sie über die Statusänderungen der Sitzung informiert werden möchten, können Sie einen SessionManagerListener implementieren. In diesem Beispiel wird die Verfügbarkeit einer CastSession in einer Activity beobachtet.

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
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }

    override fun onResume() {
        super.onResume()
        mCastSession = mSessionManager.currentCastSession
    }

    override fun onDestroy() {
        super.onDestroy()
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }
}
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();
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mCastSession = mSessionManager.getCurrentCastSession();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class);
    }
}

Stream-Übertragung

Das Beibehalten des Sitzungsstatus ist die Grundlage für die Stream-Übertragung, bei der Nutzer vorhandene Audio- und Videostreams per Sprachbefehl, über die Google Home App oder über Smart Displays auf andere Geräte übertragen können. Die Medienwiedergabe wird auf einem Gerät (der Quelle) beendet und auf einem anderen Gerät (dem Ziel) fortgesetzt. Jedes Cast-Gerät mit der neuesten Firmware kann als Quelle oder Ziel bei einer Stream-Übertragung dienen.

Wenn Sie das neue Zielgerät während einer Stream-Übertragung oder ‑Erweiterung erhalten möchten, registrieren Sie einen Cast.Listener mit CastSession#addCastListener. Rufen Sie dann während des onDeviceNameChanged Callbacks auf.CastSession#getCastDevice()

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

Automatische Wiederverbindung

Das Framework bietet einen ReconnectionService , der von der Sender-App aktiviert werden kann, um die Wiederverbindung in vielen subtilen Grenzfällen zu verarbeiten, z. B.:

  • Wiederherstellung nach einem vorübergehenden Verlust der WLAN-Verbindung
  • Wiederherstellung nach dem Wechsel in den Ruhemodus
  • Wiederherstellung nach dem Wechsel der App in den Hintergrund
  • Wiederherstellung nach einem Absturz der App

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

Dieser Dienst kann automatisch in das Manifest Ihrer App eingefügt werden, wenn die automatische Zusammenführung in Ihrer Gradle-Datei aktiviert ist.

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

Funktionsweise der Mediensteuerung

Im Cast-Framework wird die RemoteMediaPlayer Klasse aus Cast 2.x zugunsten einer neuen Klasse RemoteMediaClient, die dieselbe Funktionalität in einer Reihe praktischerer APIs bietet und die Übergabe eines GoogleApiClient vermeidet, verworfen.

Wenn Ihre App eine CastSession mit einer Web Receiver App herstellt, die den Medien-Namespace unterstützt, wird automatisch eine Instanz von RemoteMediaClient vom Framework erstellt. Ihre App kann darauf zugreifen, indem sie die Methode getRemoteMediaClient() für die CastSession Instanz aufruft.

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

Es wird erwartet, dass die Instanz von RemoteMediaClient von mehreren Teilen Ihrer App gemeinsam genutzt wird, und zwar auch von einigen internen Komponenten des Frameworks wie den persistenten Mini-Controllern und dem Benachrichtigungsdienst. Daher unterstützt diese Instanz die Registrierung mehrerer Instanzen von RemoteMediaClient.Listener.

Medienmetadaten festlegen

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

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))));

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

Medien laden

Ihre App kann ein Medienelement laden, wie im folgenden Code gezeigt. Verwenden Sie zuerst MediaInfo.Builder mit den Metadaten der Medien, um eine MediaInfo Instanz zu erstellen. Rufen Sie RemoteMediaClient aus der aktuellen CastSession ab und laden Sie dann MediaInfo in diesen RemoteMediaClient. Verwenden Sie RemoteMediaClient, um eine Medienplayer-App, die auf dem Web Receiver ausgeführt wird, abzuspielen, zu pausieren und anderweitig zu steuern.

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 auch im Abschnitt zur Verwendung von Medientracks.

4K-Videoformat

Wenn Sie prüfen möchten, in welchem Videoformat Ihre Medien vorliegen, verwenden Sie getVideoInfo() in MediaStatus, um die aktuelle Instanz von VideoInfoabzurufen. Diese Instanz enthält den Typ des HDR-TV-Formats sowie die Höhe und Breite des Displays in Pixeln. Varianten des 4K-Formats werden durch die Konstanten HDR_TYPE_* angegeben.

Benachrichtigungen zur Fernbedienung an mehrere Geräte

Wenn ein Nutzer streamt, erhalten andere Android-Geräte im selben Netzwerk eine Benachrichtigung, damit sie auch die Wiedergabe steuern können. Jeder, dessen Gerät solche Benachrichtigungen erhält, kann sie für dieses Gerät in der Einstellungen App unter „Google“ > „Google Cast“ > Benachrichtigungen zur Fernbedienung anzeigen deaktivieren. Die Benachrichtigungen enthalten eine Verknüpfung zur Einstellungen App. Weitere Informationen finden Sie unter Benachrichtigungen zur Fernbedienung von Cast.

Mini-Controller hinzufügen

Gemäß der Cast-Design Checkliste, sollte eine Sender-App ein persistentes Steuerelement namens Mini Controller bereitstellen, das angezeigt wird, wenn der Nutzer die aktuelle Inhaltsseite verlässt und zu einem anderen Teil der Sender-App wechselt. Der Mini-Controller erinnert den Nutzer an die aktuelle Cast-Sitzung. Durch Tippen auf den Mini-Controller kann der Nutzer zur erweiterten Vollbildansicht des Cast-Controllers zurückkehren.

Das Framework bietet eine benutzerdefinierte Ansicht, MiniControllerFragment, die Sie unten in der Layoutdatei jeder Aktivität hinzufügen können, in der der Mini-Controller angezeigt werden soll.

<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 Ihre Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Schaltfläche zum Abspielen/Beenden anstelle der Schaltfläche zum Abspielen/Pausieren im Mini-Controller an.

Informationen zum Festlegen des Erscheinungsbilds des Titels und Untertitels dieser benutzerdefinierten Ansicht, sowie zum Auswählen von Schaltflächen finden Sie unter Mini-Controller anpassen.

Erweiterten Controller hinzufügen

Die Google Cast-Design-Checkliste erfordert, dass eine Sender-App einen erweiterten Controller für die gestreamten Medien bereitstellt. Der erweiterte Controller ist eine Vollbildversion des Mini-Controllers.

Das Cast SDK bietet ein Widget für den erweiterten Controller namens ExpandedControllerActivity. Dies ist eine abstrakte Klasse, die Sie unterteilen müssen, um eine Cast-Schaltfläche hinzuzufügen.

Erstellen Sie zuerst eine neue Menüressourcendatei für den erweiterten Controller, um die Cast-Schaltfläche bereitzustellen:

<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.

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;
    }
}

Deklarieren Sie nun Ihre neue Aktivität im App-Manifest innerhalb des Tags application:

<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 CastOptionsProvider und ändern Sie NotificationOptions und CastMediaOptions, um die Zielaktivität auf Ihre neue Aktivität festzulegen:

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 Methode loadRemoteMedia von LocalPlayerActivity, um Ihre neue Aktivität anzuzeigen, wenn die Remote-Medien geladen werden:

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 Ihre Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Schaltfläche zum Abspielen/Beenden anstelle der Schaltfläche zum Abspielen/Pausieren im erweiterten Controller an.

Informationen zum Festlegen des Erscheinungsbilds mit Designs, zum Auswählen der anzuzeigenden Schaltflächen und zum Hinzufügen benutzerdefinierter Schaltflächen finden Sie unter Erweiterten Controller anpassen.

Lautstärkeregelung

Das Framework verwaltet die Lautstärke für die Sender-App automatisch. Das Framework synchronisiert die Sender- und Web Receiver App automatisch, sodass die Benutzeroberfläche des Senders immer die vom Web Receiver angegebene Lautstärke meldet.

Lautstärkeregelung über physische Tasten

Unter Android können die physischen Tasten auf dem Sendergerät standardmäßig verwendet werden, um die Lautstärke der Cast-Sitzung auf dem Web Receiver zu ändern. Dies gilt für alle Geräte mit Jelly Bean oder höher.

Lautstärkeregelung über physische Tasten vor Jelly Bean

Wenn Sie die physischen Lautstärketasten verwenden möchten, um die Lautstärke des Web Receiver-Geräts auf Android-Geräten vor Jelly Bean zu regeln, sollte die Sender-App dispatchKeyEvent in ihren Aktivitäten überschreiben und CastContext.onDispatchVolumeKeyEventBeforeJellyBean()aufrufen:

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);
    }
}

Mediensteuerung zu Benachrichtigungen und Sperrbildschirm hinzufügen

Nur unter Android erfordert die Google Cast-Design-Checkliste, dass eine Sender-App die Mediensteuerung in einer Benachrichtigung und auf dem Sperrbildschirm implementiert, wenn der Sender streamt, die Sender-App aber nicht im Fokus ist. Das Framework bietet MediaNotificationService und MediaIntentReceiver , mit denen die Sender-App die Mediensteuerung in einer Benachrichtigung und auf dem Sperr bildschirm erstellen kann.

MediaNotificationService wird ausgeführt, wenn der Sender streamt, und zeigt eine Benachrichtigung mit einem Bild-Thumbnail und Informationen zum aktuellen Streamingelement, eine Schaltfläche zum Abspielen/Pausieren und eine Schaltfläche zum Beenden an.

MediaIntentReceiver ist ein BroadcastReceiver, der Nutzeraktionen aus der Benachrichtigung verarbeitet.

Ihre App kann Benachrichtigungen und die Mediensteuerung über den Sperrbildschirm über NotificationOptions konfigurieren. Ihre App kann konfigurieren, welche Steuerungsschaltflächen in der Benachrichtigung angezeigt werden und welche Activity geöffnet werden soll, wenn der Nutzer auf die Benachrichtigung tippt. Wenn keine Aktionen explizit angegeben werden, werden die Standardwerte MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK und MediaIntentReceiver.ACTION_STOP_CASTING verwendet.

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 über Benachrichtigungen und den Sperrbildschirm ist standardmäßig aktiviert und kann deaktiviert werden, indem setNotificationOptions mit „null“ in CastMediaOptions.Builderaufgerufen wird. Derzeit ist die Funktion für den Sperrbildschirm aktiviert, solange die Benachrichtigung aktiviert ist.

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 Ihre Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Schaltfläche zum Abspielen/Beenden anstelle der Schaltfläche zum Abspielen/Pausieren in der Benachrichtigungssteuerung, aber nicht in der Steuerung auf dem Sperrbildschirm an.

Hinweis: Wenn Sie Steuerelemente auf dem Sperrbildschirm auf Geräten vor Lollipop anzeigen möchten, RemoteMediaClient fordert automatisch den Audiofokus in Ihrem Namen an.

Fehler verarbeiten

Es ist sehr wichtig, dass Sender-Apps alle Fehler-Callbacks verarbeiten und die beste Antwort für jede Phase des Cast-Lebenszyklus festlegen. Die App kann dem Nutzer Fehlerdialogfelder anzeigen oder die Verbindung zum Web Receiver beenden.