Cast'i Android Uygulamanıza Entegre Edin

Bu geliştirici kılavuzunda, Android Sender SDK'sını kullanarak Android gönderen uygulamanıza Google Cast desteğini nasıl ekleyeceğiniz açıklanmaktadır.

Mobil cihaz veya dizüstü bilgisayar, oynatmayı kontrol eden gönderen, Google Cast cihazı ise içeriği TV'de gösteren alıcıdır.

Gönderen çerçevesi, gönderende çalışma zamanında bulunan Cast sınıf kitaplığı ikilisi ve ilişkili kaynakları ifade eder. Gönderen uygulaması veya Yayın uygulaması, gönderende de çalışan bir uygulamayı ifade eder. Web alıcı uygulaması, Cast özellikli cihazda çalışan HTML uygulamasını ifade eder.

Gönderen çerçevesi, gönderen uygulamasını etkinlikler hakkında bilgilendirmek ve Cast uygulamasının yaşam döngüsünün çeşitli durumları arasında geçiş yapmak için eşzamansız geri çağırma tasarımı kullanır.

Uygulama akışı

Aşağıdaki adımlarda, gönderen Android uygulaması için tipik üst düzey yürütme akışı açıklanmaktadır:

  • Cast çerçevesi, MediaRouterActivity yaşam döngüsüne göre cihaz keşfini otomatik olarak başlatır.
  • Kullanıcı Yayınla düğmesini tıkladığında çerçeve, bulunan yayın cihazlarının listesini içeren Yayınla iletişim kutusunu gösterir.
  • Kullanıcı bir yayın cihazı seçtiğinde çerçeve, yayın cihazında Web Alıcı uygulamasını başlatmaya çalışır.
  • Çerçeve, Web Receiver uygulamasının başlatıldığını onaylamak için gönderen uygulamasında geri çağırmaları tetikler.
  • Çerçeve, gönderen ve Web Alıcı uygulamaları arasında bir iletişim kanalı oluşturur.
  • Çerçeve, Web Alıcı'da medya oynatmayı yüklemek ve kontrol etmek için iletişim kanalını kullanır.
  • Çerçeve, gönderen ile Web Alıcı arasındaki medya oynatma durumunu senkronize eder: Kullanıcı, gönderen kullanıcı arayüzünde işlemler yaptığında çerçeve, bu medya kontrol isteklerini Web Alıcı'ya iletir. Web Alıcı, medya durumu güncellemeleri gönderdiğinde ise çerçeve, gönderen kullanıcı arayüzünün durumunu günceller.
  • Kullanıcı, yayın cihazıyla bağlantıyı kesmek için Yayınla düğmesini tıkladığında çerçeve, gönderen uygulamanın Web Alıcı ile bağlantısını keser.

Google Cast Android SDK'sındaki tüm sınıfların, yöntemlerin ve etkinliklerin kapsamlı bir listesi için Google Cast Sender API Reference for Android başlıklı makaleyi inceleyin. Aşağıdaki bölümlerde, Android uygulamanıza Cast'i ekleme adımları açıklanmaktadır.

Android manifest dosyasını yapılandırma

Uygulamanızın AndroidManifest.xml dosyasında, Cast SDK'sı için aşağıdaki öğeleri yapılandırmanız gerekir:

uses-sdk

Cast SDK'nın desteklediği minimum ve hedef Android API düzeylerini ayarlayın. Şu anda minimum API düzeyi 23, hedef API düzeyi ise 34'tür.

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

android:theme

Uygulamanızın temasını minimum Android SDK sürümüne göre ayarlayın. Örneğin, kendi temanızı uygulamıyorsanız Lollipop öncesi bir minimum Android SDK sürümünü hedeflerken Theme.AppCompat varyantını kullanmanız gerekir.

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

Yayınlama Bağlamını Başlatma

Çerçevede, tüm çerçeve etkileşimlerini koordine eden CastContext adlı genel tekil nesne bulunur.

Uygulamanız, CastContext tekilini başlatmak için gereken seçenekleri sağlamak üzere OptionsProvider arayüzünü uygulamalıdır. OptionsProvider, çerçeve davranışını etkileyen seçenekleri içeren CastOptions örneğini sağlar. Bunlardan en önemlisi, keşif sonuçlarını filtrelemek ve Cast oturumu başlatıldığında Web Receiver uygulamasını başlatmak için kullanılan Web Receiver uygulama kimliğidir.

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

Uygulanan OptionsProvider öğesinin tam nitelikli adını, gönderen uygulamanın AndroidManifest.xml dosyasında meta veri alanı olarak beyan etmeniz gerekir:

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

CastContext, CastContext.getSharedInstance() çağrıldığında geç başlatılır.

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

Yayınlama kullanıcı deneyimi widget'ları

Cast çerçevesi, Cast Tasarım Kontrol Listesi'ne uygun widget'lar sağlar:

  • Tanıtım Yerleşimi: Çerçeve, alıcı ilk kez kullanılabilir olduğunda kullanıcıya Cast düğmesine dikkat çekmek için gösterilen özel bir Görünüm sağlar. IntroductoryOverlay, Gönderen uygulaması, metni ve başlık metninin konumunu özelleştirebilir.

  • Yayınla düğmesi: Yayınla düğmesi, Yayın cihazlarının kullanılabilirliğinden bağımsız olarak görünür. Kullanıcı ilk kez Yayınla düğmesini tıkladığında, keşfedilen cihazların listelendiği bir Yayınla iletişim kutusu gösterilir. Kullanıcı, cihaz bağlıyken Yayınla düğmesini tıkladığında mevcut medya meta verileri (ör. başlık, kayıt stüdyosunun adı ve küçük resim) gösterilir veya kullanıcının yayın cihazıyla bağlantısını kesmesine izin verilir. "Yayınla düğmesi" bazen "Yayınla simgesi" olarak da adlandırılır.

  • Mini denetleyici: Kullanıcı içerik yayınlarken ve mevcut içerik sayfasından veya genişletilmiş denetleyiciden gönderen uygulamasındaki başka bir ekrana giderken, mini denetleyici ekranın alt kısmında gösterilir. Bu sayede kullanıcı, şu anda yayınlanan medya meta verilerini görebilir ve oynatmayı kontrol edebilir.

  • Genişletilmiş Denetleyici: Kullanıcı içerik yayınlarken medya bildirimini veya mini denetleyiciyi tıkladığında, genişletilmiş denetleyici başlatılır. Bu denetleyicide, oynatılan medya meta verileri gösterilir ve medya oynatmayı kontrol etmek için çeşitli düğmeler bulunur.

  • Bildirim: Yalnızca Android'de kullanılabilir. Kullanıcı içerik yayınlarken gönderen uygulamasından uzaklaştığında, şu anda yayınlanan medya meta verilerini ve oynatma kontrollerini gösteren bir medya bildirimi görüntülenir.

  • Kilit ekranı: Yalnızca Android'de kullanılabilir. Kullanıcı içerik yayınlarken kilit ekranına gittiğinde (veya cihazın zaman aşımı süresi dolduğunda) şu anda yayınlanan medya meta verilerini ve oynatma kontrollerini gösteren bir medya kilit ekranı kontrolü görüntülenir.

Aşağıdaki kılavuzda bu widget'ların uygulamanıza nasıl ekleneceği açıklanmaktadır.

Yayın düğmesi ekleme

Android MediaRouter API'leri, ikincil cihazlarda medya görüntüleme ve oynatma işlemlerini etkinleştirmek için tasarlanmıştır. MediaRouter API'sini kullanan Android uygulamaları, kullanıcıların medya içeriğini Cast cihazı gibi ikincil bir cihazda oynatmak için medya rotası seçmesine olanak tanımak amacıyla kullanıcı arayüzlerinde Yayın düğmesi bulundurmalıdır.

Bu çerçeve, MediaRouteButton öğesini Cast button olarak eklemeyi çok kolaylaştırır. Öncelikle menünüzü tanımlayan XML dosyasına bir menü öğesi veya MediaRouteButton eklemeniz ve bunu çerçeveye bağlamak için CastButtonFactory kullanmanız gerekir.

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

Ardından, Activity FragmentActivity öğesinden devralıyorsa düzeninize MediaRouteButton ekleyebilirsiniz.

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

Yayınla düğmesinin görünümünü tema kullanarak ayarlamak için Yayınla düğmesini özelleştirme başlıklı makaleye bakın.

Cihaz bulmayı yapılandırma

Cihaz keşfi tamamen CastContext tarafından yönetilir. Gönderen uygulama, CastContext'i başlatırken Web Alıcı uygulama kimliğini belirtir ve isteğe bağlı olarak CastOptions içinde supportedNamespaces ayarlanarak ad alanı filtreleme isteğinde bulunabilir. CastContext, dahili olarak MediaRouter öğesine referans içerir ve aşağıdaki koşullarda keşif sürecini başlatır:

  • Cihaz keşfi gecikmesini ve pil kullanımını dengelemek için tasarlanmış bir algoritmaya göre, gönderen uygulama ön plana girdiğinde keşif bazen otomatik olarak başlatılır.
  • Yayın iletişim kutusu açık olmalıdır.
  • Cast SDK, Cast oturumunu kurtarmaya çalışıyor.

Yayın iletişim kutusu kapatıldığında veya gönderen uygulama arka plana girdiğinde bulma işlemi durdurulur.

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

Oturum yönetiminin işleyiş şekli

Cast SDK, Cast oturumu kavramını sunar. Bu oturumun oluşturulması, bir cihaza bağlanma, Web Alıcı uygulamasını başlatma (veya uygulamaya katılma), bu uygulamaya bağlanma ve medya kontrol kanalını başlatma adımlarını birleştirir. Cast oturumları ve Web Alıcı yaşam döngüsü hakkında daha fazla bilgi için Web Alıcı Uygulama yaşam döngüsü kılavuzu'na bakın.

Oturumlar, uygulamanızın CastContext.getSessionManager() üzerinden erişebileceği SessionManager sınıfı tarafından yönetilir. Bireysel oturumlar, Session sınıfının alt sınıflarıyla temsil edilir. Örneğin, CastSession yayın cihazlarıyla yapılan oturumları gösterir. Uygulamanız, SessionManager.getCurrentCastSession() aracılığıyla şu anda etkin olan Cast oturumuna erişebilir.

Uygulamanız, oturum etkinliklerini (ör. oluşturma, askıya alma, devam ettirme ve sonlandırma) izlemek için SessionManagerListener sınıfını kullanabilir. Çerçeve, bir oturum etkin durumdayken anormal/ani bir sonlandırmadan sonra otomatik olarak devam etmeyi dener.

Oturumlar, MediaRouter iletişim kutularındaki kullanıcı hareketlerine yanıt olarak otomatik şekilde oluşturulur ve sonlandırılır.

Uygulamalar, Cast başlatma hatalarını daha iyi anlamak için oturum başlatma hatasını CastContext#getCastReasonCodeForCastStatusCode(int) kullanarak CastReasonCodes biçimine dönüştürebilir. Bazı oturum başlatma hatalarının (ör. CastReasonCodes#CAST_CANCELLED) beklenen davranış olduğunu ve hata olarak günlüğe kaydedilmemesi gerektiğini lütfen unutmayın.

Oturumun durum değişikliklerinden haberdar olmanız gerekiyorsa SessionManagerListener uygulayabilirsiniz. Bu örnek, bir Activity içinde CastSession ürününün stok durumunu dinler.

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

Akış aktarma

Oturum durumunu koruma, akış aktarımının temelini oluşturur. Bu özellik sayesinde kullanıcılar, sesli komutları, Google Home uygulaması veya akıllı ekranları kullanarak mevcut ses ve video akışlarını cihazlar arasında taşıyabilir. Medya, bir cihazda (kaynak) oynatmayı durdurur ve başka bir cihazda (hedef) oynatmaya devam eder. En yeni donanım yazılımına sahip tüm Cast cihazlar, akış aktarımında kaynak veya hedef olarak kullanılabilir.

Yayın aktarımı veya genişletme sırasında yeni hedef cihazı almak için CastSession#addCastListener kullanarak Cast.Listener cihazı kaydedin. Ardından, CastSession#getCastDevice() numaralı telefonu onDeviceNameChanged geri arama sırasında arayın.

Daha fazla bilgi için Web alıcısında akış aktarımı başlıklı makaleyi inceleyin.

Otomatik yeniden bağlanma

Çerçeve, gönderen uygulama tarafından etkinleştirilebilen bir ReconnectionService sağlar. Bu, yeniden bağlantı işlemini aşağıdaki gibi birçok ince uç durumda ele almak için kullanılabilir:

  • Geçici Wi-Fi kaybından kurtulma
  • Cihaz uyku modundan kurtarma
  • Uygulamayı arka plana aldıktan sonra kurtarma
  • Uygulama kilitlendiyse kurtarma

Bu hizmet varsayılan olarak etkindir ve CastOptions.Builder bölümünden devre dışı bırakılabilir.

Bu hizmet, gradle dosyanızda otomatik birleştirme etkinse uygulamanızın manifestine otomatik olarak birleştirilebilir.

Çerçeve, medya oturumu olduğunda hizmeti başlatır ve medya oturumu sona erdiğinde durdurur.

Medya Denetimi'nin işleyiş şekli

Cast çerçevesi, daha kullanışlı bir API grubuyla aynı işlevselliği sağlayan ve GoogleApiClient'ın iletilmesini gerektirmeyen yeni bir sınıf RemoteMediaClient lehine Cast 2.x'teki RemoteMediaPlayer sınıfını kullanımdan kaldırıyor.

Uygulamanız, medya ad alanını destekleyen bir web alıcı uygulamasıyla CastSession oluşturduğunda çerçeve tarafından otomatik olarak RemoteMediaClient örneği oluşturulur. Uygulamanız, CastSession örneğinde getRemoteMediaClient() yöntemini çağırarak bu örneğe erişebilir.

Web alıcısına istek gönderen tüm RemoteMediaClient yöntemleri, bu isteği izlemek için kullanılabilecek bir PendingResult nesnesi döndürür.

RemoteMediaClient örneğinin, uygulamanızın birden fazla bölümü ve hatta çerçevedeki bazı dahili bileşenler (ör. kalıcı mini denetleyiciler ve bildirim hizmeti) tarafından paylaşılması beklenir. Bu nedenle, bu örnek RemoteMediaClient.Listener öğesinin birden fazla örneğinin kaydedilmesini destekler.

Medya meta verilerini ayarlama

MediaMetadata sınıfı, yayınlamak istediğiniz bir medya öğesiyle ilgili bilgileri temsil eder. Aşağıdaki örnekte bir filmin yeni bir MediaMetadata örneği oluşturulur ve başlık, altyazı ve iki resim ayarlanır.

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

Medya meta verileriyle resimlerin kullanımı hakkında bilgi edinmek için Resim Seçimi bölümüne bakın.

Medya yükleme

Uygulamanız, aşağıdaki kodda gösterildiği gibi bir medya öğesi yükleyebilir. MediaInfo.Builder örneği oluşturmak için önce medyayla ilgili meta verilerle birlikte MediaInfo kullanın. Mevcut CastSession'den RemoteMediaClient'yi alın, ardından MediaInfo'yi bu RemoteMediaClient'ye yükleyin. Web alıcısında çalışan bir medya oynatıcı uygulamasını oynatmak, duraklatmak ve başka şekillerde kontrol etmek için RemoteMediaClient simgesini kullanın.

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

Ayrıca medya parçalarını kullanma bölümüne de bakın.

4K video biçimi

Medyanızın video biçimini kontrol etmek için getVideoInfo() kullanarak VideoInfo öğesinin mevcut örneğini MediaStatus'ta alın. Bu örnekte HDR TV biçiminin türü ve ekranın piksel cinsinden yüksekliği ve genişliği yer alır. 4K biçiminin varyantları HDR_TYPE_* sabitleriyle gösterilir.

Birden fazla cihazda uzaktan kumanda bildirimleri

Bir kullanıcı yayın yaparken aynı ağdaki diğer Android cihazlar, oynatmayı kontrol etmelerine olanak tanıyan bir bildirim alır. Cihazı bu tür bildirimler alan herkes, Google > Google Cast > Uzaktan kumanda bildirimlerini göster bölümündeki Ayarlar uygulamasından ilgili cihaz için bu bildirimleri devre dışı bırakabilir. (Bildirimlerde Ayarlar uygulamasına giden bir kısayol bulunur.) Daha fazla bilgi için Yayınlama uzaktan kumanda bildirimleri başlıklı makaleyi inceleyin.

Mini kumanda ekleme

Cast Tasarım Kontrol Listesi'ne göre, gönderen uygulaması, kullanıcının mevcut içerik sayfasından gönderen uygulamasının başka bir bölümüne gittiğinde görünmesi gereken mini denetleyici olarak bilinen kalıcı bir denetim sağlamalıdır. Mini denetleyici, mevcut Cast oturumunu kullanıcıya görünür bir şekilde hatırlatır. Mini kumandaya dokunarak kullanıcı, Cast'in tam ekran genişletilmiş kumanda görünümüne dönebilir.

Çerçeve, mini denetleyiciyi göstermek istediğiniz her etkinliğin düzen dosyasına ekleyebileceğiniz özel bir Görünüm olan MiniControllerFragment'ı sağlar.

<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" />

Gönderen uygulamanız bir video veya ses canlı yayını oynatırken SDK, mini oynatıcıdaki oynat/duraklat düğmesi yerine otomatik olarak bir oynat/durdur düğmesi gösterir.

Bu özel görünümün başlığının ve alt başlığının metin görünümünü ayarlamak ve düğmeleri seçmek için Mini denetleyiciyi özelleştirme başlıklı makaleyi inceleyin.

Genişletilmiş denetleyici ekleme

Google Cast Tasarım Kontrol Listesi, gönderen uygulamanın Cast edilen medya için genişletilmiş denetleyici sağlamasını zorunlu kılar. Genişletilmiş denetleyici, mini denetleyicinin tam ekran sürümüdür.

Cast SDK, genişletilmiş denetleyici için ExpandedControllerActivity adlı bir widget sağlar. Bu, Cast düğmesi eklemek için alt sınıf oluşturmanız gereken soyut bir sınıftır.

İlk olarak, Cast düğmesini sağlamak için genişletilmiş denetleyiciye yönelik yeni bir menü kaynağı dosyası oluşturun:

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

ExpandedControllerActivity öğesini genişleten yeni bir sınıf oluşturun.

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

Şimdi yeni etkinliğinizi application etiketi içindeki uygulama manifestinde beyan edin:

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

CastOptionsProvider simgesini düzenleyin ve hedef etkinliği yeni etkinliğiniz olarak ayarlamak için NotificationOptions ve CastMediaOptions simgelerini değiştirin:

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

Uzak medya yüklendiğinde yeni etkinliğinizi göstermek için LocalPlayerActivity loadRemoteMedia yöntemini güncelleyin:

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

Gönderen uygulamanız bir video veya ses canlı yayını oynatırken SDK, genişletilmiş kontrol panelinde oynat/duraklat düğmesi yerine otomatik olarak oynat/durdur düğmesini gösterir.

Temaları kullanarak görünümü ayarlamak için hangi düğmelerin gösterileceğini seçin ve özel düğmeler ekleyin. Genişletilmiş Denetleyici'yi özelleştirme başlıklı makaleyi inceleyin.

Ses düzeyi kontrolü

Çerçeve, gönderen uygulamasının ses düzeyini otomatik olarak yönetir. Çerçeve, gönderen ve Web Receiver uygulamalarını otomatik olarak senkronize eder. Böylece gönderen kullanıcı arayüzü her zaman Web Receiver tarafından belirtilen ses düzeyini bildirir.

Fiziksel düğmeyle ses kontrolü

Android'de, gönderen cihazdaki fiziksel düğmeler, Jelly Bean veya daha yeni bir sürümü kullanan tüm cihazlarda Web Alıcı'daki yayın oturumunun ses düzeyini varsayılan olarak değiştirmek için kullanılabilir.

Jelly Bean'den önceki sürümlerde fiziksel düğmeyle ses kontrolü

Jelly Bean'den eski Android cihazlarda Web Alıcı cihazın sesini kontrol etmek için fiziksel ses tuşlarını kullanmak istiyorsanız gönderen uygulama, Etkinliklerindeki dispatchKeyEvent'yi geçersiz kılmalı ve CastContext.onDispatchVolumeKeyEventBeforeJellyBean()'i çağırmalıdır:

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

Bildirim ve kilit ekranına medya kontrolleri ekleme

Yalnızca Android'de Google Cast Tasarım Kontrol Listesi, gönderen uygulamasının bildirimde ve kilit ekranında medya kontrollerini uygulaması gerektiğini belirtir. Bu durumda gönderen yayın yapmaktadır ancak gönderen uygulaması odaklanmamıştır. Bu çerçeve, gönderen uygulamanın bildirimde ve kilit ekranında medya kontrolleri oluşturmasına yardımcı olmak için MediaNotificationService ve MediaIntentReceiver sağlar.

MediaNotificationService, gönderen yayın yaparken çalışır ve mevcut yayın öğesiyle ilgili resim küçük resmi ve bilgileri, oynatma/duraklatma düğmesi ve durdurma düğmesi içeren bir bildirim gösterir.

MediaIntentReceiver, bildirimdeki kullanıcı işlemlerini işleyen bir BroadcastReceiver'dir.

Uygulamanız, NotificationOptions aracılığıyla kilit ekranından bildirim ve medya kontrolünü yapılandırabilir. Uygulamanız, bildirimde hangi kontrol düğmelerinin gösterileceğini ve kullanıcı bildirime dokunduğunda hangi Activity'nin açılacağını yapılandırabilir. İşlemler açıkça sağlanmazsa varsayılan değerler olan MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK ve MediaIntentReceiver.ACTION_STOP_CASTING kullanılır.

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

Bildirim ve kilit ekranında medya denetimlerinin gösterilmesi varsayılan olarak etkindir ve setNotificationOptions, CastMediaOptions.Builder içinde null ile çağrılarak devre dışı bırakılabilir. Şu anda, bildirimler açık olduğu sürece kilit ekranı özelliği de açıktır.

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

Gönderen uygulamanız bir video veya ses canlı yayını oynatırken SDK, bildirim kontrolündeki oynat/duraklat düğmesi yerine otomatik olarak bir oynat/durdur düğmesi gösterir ancak kilit ekranı kontrolünde göstermez.

Not: Kilit ekranı kontrollerini Lollipop öncesi cihazlarda göstermek için, RemoteMediaClient sizin adınıza otomatik olarak ses odağı isteğinde bulunur.

Hataları işleme

Gönderen uygulamaların tüm hata geri aramalarını işlemesi ve Cast yaşam döngüsünün her aşaması için en iyi yanıtı belirlemesi çok önemlidir. Uygulama, kullanıcıya hata iletişim kutuları gösterebilir veya Web Alıcı ile bağlantıyı sonlandırmaya karar verebilir.