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örüntüleyen Alıcı'dır.

Gönderen çerçevesi, göndericinin çalışma zamanında mevcut olan Cast sınıfı kitaplığı ikili programını ve ilişkili kaynakları ifade eder. Gönderen uygulaması veya Cast uygulaması, gönderende de çalışan bir uygulamayı belirtir. Web Alıcısı uygulaması, Cast uyumlu cihazda çalışan HTML uygulamasını belirtir.

Gönderen çerçevesi, gönderen uygulamayı 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 bir geri çağırma tasarımı kullanır.

Uygulama akışı

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

  • Cast çerçevesi, Activity yaşam döngüsüne göre otomatik olarak MediaRouter cihaz keşfini başlatır.
  • Kullanıcı Yayınla düğmesini tıkladığında çerçeve, keşfedilen 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ısı uygulamasını başlatmayı dener.
  • Çerçeve, Web Alıcısı uygulamasının başlatıldığını onaylamak için gönderen uygulamada geri çağırmaları çağırır.
  • Çerçeve, gönderen ve Web Alıcı uygulamaları arasında bir iletişim kanalı oluşturur.
  • Çerçeve, Web Alıcısı'na medya içeriği oynatmak ve oynatmak için iletişim kanalını kullanır.
  • Çerçeve, gönderen ve Web Alıcı arasında medya oynatma durumunu senkronize eder: Kullanıcı, gönderen kullanıcı arayüzü işlemleri yaptığında, çerçeve bu medya denetimi isteklerini Web Alıcısına iletir ve Web Alıcısı medya durumu güncellemelerini gönderdiğinde çerçeve, gönderenin kullanıcı arayüzünün durumunu günceller.
  • Kullanıcı, Yayın cihazıyla olan bağlantıyı kesmek için Yayınla düğmesini tıkladığında çerçeve, gönderen uygulamanın Web Alıcısı 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 Android için Google Cast Sender API Reference (Android için Google Cast Gönderen API Referansı) bölümüne bakın. Aşağıdaki bölümlerde, Cast'i Android uygulamanıza ekleme adımları ele alınmaktadır.

Android manifest dosyasını yapılandırma

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

uses-sdk

Cast SDK'nın desteklediği minimum ve hedef Android API düzeylerini ayarlayın. Şu anda minimum değer API düzeyi 21, hedef ise API düzeyi 28'dir.

<uses-sdk
        android:minSdkVersion="21"
        android:targetSdkVersion="28" />

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 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ın Bağlamını ilk kullanıma hazırlayın

Çerçevede, çerçevenin tüm etkileşimlerini koordine eden global bir tekil nesne (CastContext) bulunur.

CastContext single'ını başlatmak için gereken seçenekleri sağlamak amacıyla uygulamanız OptionsProvider arayüzünü uygulamalıdır. OptionsProvider, çerçevenin davranışını etkileyen seçenekleri içeren CastOptions örneğini sağlar. Bunların en önemlisi, keşif sonuçlarını filtrelemek ve bir Yayın oturumu başlatıldığında Web Alıcısı uygulamasını başlatmak için kullanılan Web Alıcısı 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 bir 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.getSharedInstance() çağrıldığında CastContext, geç olarak 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ın Kullanıcı Deneyimi Widget'ları

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

  • Tanıtım Yer Paylaşımı: Çerçeve, bir alıcı ilk kez kullanılabilir olduğunda Yayınla düğmesine dikkat çekmek için kullanıcıya gösterilen özel bir Görünüm IntroductoryOverlay sağlar. Sender 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ı Yayınla düğmesini ilk kez tıkladığında, keşfedilen cihazları listeleyen bir Yayın iletişim kutusu gösterilir. Cihaz bağlıyken kullanıcı Yayınla düğmesini tıkladığında, geçerli medya meta verilerini (başlık, kayıt stüdyosunun adı ve küçük resim gibi) görüntüler veya kullanıcının Yayın cihazıyla olan bağlantısını kesmesine olanak tanır. "Yayınla düğmesi" bazen "Yayınla simgesi" olarak da adlandırılır.

  • Mini Denetleyici: Kullanıcı içerik yayınlarken mevcut içerik sayfasından veya genişletilmiş denetleyiciden gönderen uygulamasında başka bir ekrana gittiğinde mini denetleyici, kullanıcının yayınlanan medya meta verilerini görmesine ve oynatmayı kontrol etmesine olanak tanımak için ekranın alt kısmında görüntülenir.

  • Genişletilmiş Denetleyici: Kullanıcı içerik yayınlarken medya bildirimini veya mini denetleyiciyi tıklarsa genişletilmiş kumanda başlatılır. Bu kontrol, şu anda oynatılan medya meta verilerini görüntüler ve medya oynatmayı kontrol etmek için çeşitli düğmeler sağlar.

  • Bildirim: Yalnızca Android. Kullanıcı içerik yayınlarken gönderen uygulamasından çıktığında, yayınlanmakta olan medya meta verilerini ve oynatma kontrollerini gösteren bir medya bildirimi görüntülenir.

  • Kilit Ekranı: Yalnızca Android. Kullanıcı içerik yayınlarken ve kilit ekranına gittiğinde (veya cihaz zaman aşımına uğradığında) şu anda yayınlanan medya meta verilerini ve oynatma kontrollerini gösteren bir medya kilidi ekranı kontrolü görüntülenir.

Aşağıdaki kılavuzda, bu widget'ların uygulamanıza nasıl ekleneceğiyle ilgili açıklamalar bulunmaktadır.

Yayınla Düğmesi Ekle

Android MediaRouter API'leri, ikincil cihazlarda medya görüntüleme ve oynatma özelliğini etkinleştirmek için tasarlanmıştır. MediaRouter API'yi kullanan Android uygulamalarında, kullanıcıların bir yayın cihazı gibi ikincil bir cihazda medya oynatmak üzere bir medya rotası seçmelerine olanak tanımak için kullanıcı arayüzlerinin bir parçası olarak bir Yayınla düğmesi bulunmalıdır.

Bu çerçeve, Cast button olarak MediaRouteButton eklemeyi çok kolay hale getirir. Öncelikle menünüzü tanımlayan xml dosyasına bir menü öğesi veya MediaRouteButton eklemeniz ve CastButtonFactory kullanarak menünüzü çerçeveye bağlamanı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 öğeniz FragmentActivity öğesinden devralıyorsa düzeninize bir 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 bölümüne bakın.

Cihaz bulmayı yapılandırma

Cihaz keşfi tamamen CastContext tarafından yönetilir. Gönderen uygulaması, CastContext'i başlatırken Web Alıcısı uygulama kimliğini belirtir ve isteğe bağlı olarak CastOptions içindeki supportedNamespaces ayarını yaparak ad alanı filtrelemesi için istekte bulunabilir. CastContext, MediaRouter için dahili referans bulundurur ve keşif sürecini aşağıdaki koşullar altında başlatır:

  • Cihaz keşif gecikmesi ile pil kullanımını dengelemek için tasarlanmış bir algoritmaya dayanan keşif, bazen gönderen uygulaması ön plana girdiğinde otomatik olarak başlatılır.
  • Yayınla iletişim kutusu açık.
  • Cast SDK'sı bir Yayın oturumunu kurtarmaya çalışıyor.

Yayınlama iletişim kutusu kapatıldığında veya gönderen uygulaması arka plana girdiğinde keşif 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'sı bir cihaza bağlanma, bir Web Alıcısı uygulamasını başlatma (veya katılma), uygulamaya bağlanma ve bir medya kontrol kanalını başlatma adımlarını birleştiren Cast oturumu kavramını tanıtmaktadır. Yayın oturumları ve Web Alıcısı yaşam döngüsü hakkında daha fazla bilgi için Web Alıcısı Uygulama yaşam döngüsü kılavuzuna bakın.

Oturumlar, uygulamanızın CastContext.getSessionManager() üzerinden erişebildiği SessionManager sınıfı tarafından yönetilir. Bağımsız oturumlar, Session sınıfının alt sınıflarıyla temsil edilir. Örneğin CastSession, Yayın cihazlarıyla yapılan oturumları temsil eder. Uygulamanız, şu anda etkin olan Yayın oturumuna SessionManager.getCurrentCastSession() üzerinden erişebilir.

Uygulamanız; oluşturma, askıya alma, devam ettirme ve sonlandırma gibi oturum etkinliklerini izlemek için SessionManagerListener sınıfını kullanabilir. Çerçeve, bir oturum aktifken otomatik olarak anormal/ani sonlandırma durumundan devam ettirmeye çalışır.

Oturumlar, MediaRouter iletişim kutularından gelen kullanıcı hareketlerine yanıt olarak otomatik olarak oluşturulur ve kısaltılır.

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

Oturumla ilgili durum değişikliklerinin farkında olmanız gerekiyorsa SessionManagerListener uygulayabilirsiniz. Bu örnek, Activity içindeki bir CastSession öğesinin kullanılabilirliğini 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
    }

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

Akış aktarımı

Oturum durumunun korunması, akış aktarımının temelini oluşturur. Burada kullanıcılar, mevcut ses ve video akışlarını sesli komutlar, Google Home uygulaması veya akıllı ekranlar kullanarak cihazlar arasında taşıyabilir. Medyanın oynatılması bir cihazda (kaynak) durdurulur ve başka bir cihazda (hedef) devam eder. En yeni donanım yazılımına sahip her yayın cihazı, akış aktarımında kaynak veya hedef olarak kullanılabilir.

Akış aktarımı veya genişletme sırasında yeni hedef cihazı almak için CastSession#addCastListener kullanarak bir Cast.Listener kaydedin. Ardından onDeviceNameChanged geri çağırma işlemi sırasında CastSession#getCastDevice() numarasını arayın.

Daha fazla bilgi için Web Alıcısı'nda akış aktarımı bölümüne bakın.

Otomatik yeniden bağlanma

Çerçeve, gönderen uygulama tarafından etkinleştirilebilen bir ReconnectionService sağlar. Bu sayede, aşağıdaki gibi pek çok sorunun oluşmasına rağmen yeniden bağlantı kurulamaz:

  • Geçici kablosuz ağ kaybından kurtarma
  • Cihaz uyku modundan çık
  • Uygulamayı arka plana alarak kurtarma
  • Uygulama çöktüyse kurtarma

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

Gradle dosyanızda otomatik birleştirme etkinse bu hizmet otomatik olarak uygulamanızın manifest dosyasıyla birleştirilebilir.

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

Medya Denetimi nasıl çalışır?

Cast çerçevesi, Cast 2.x'teki RemoteMediaPlayer sınıfını sonlandırıp onun yerine yeni bir sınıf olan RemoteMediaClient sınıfını kullanıma sunuyor. Bu sınıf, daha kullanışlı bir grup API'de aynı işlevselliği sağlar ve bir GoogleApiClient'a geçme zorunluluğunu ortadan kaldırır.

Uygulamanız medya ad alanını destekleyen bir Web Alıcısı 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 buna erişebilir.

Web Alıcısı'na istek gönderen tüm RemoteMediaClient yöntemleri, söz konusu 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 aslında çerçevenin kalıcı mini denetleyiciler ve bildirim hizmeti gibi bazı dahili bileşenleri tarafından paylaşılması beklenir. Bu amaçla, bu örnek birden fazla RemoteMediaClient.Listener örneğinin kaydedilmesini destekler.

Medya meta verilerini ayarla

MediaMetadata sınıfı, Yayınlamak istediğiniz bir medya öğesiyle ilgili bilgileri temsil eder. Aşağıdaki örnek, bir filmin yeni MediaMetadata örneğini oluşturur ve başlık, alt başlık ve iki resim ayarlar.

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 verileri olan resimlerin kullanımı hakkında Resim Seçimi bölümüne bakın.

Medya yükle

Uygulamanız, aşağıdaki kodda gösterildiği gibi bir medya öğesi yükleyebilir. Önce bir MediaInfo örneği oluşturmak için medyanın meta verileriyle MediaInfo.Builder'i kullanın. Mevcut CastSession öğesinden RemoteMediaClient öğesini alın, ardından MediaInfo öğesini bu RemoteMediaClient öğesine 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 uygulamasını 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 kanallarını kullanma ile ilgili bölüme de bakın.

4K video biçimi

Medyanızın hangi video biçimini kullandığını kontrol etmek için MediaStatus'daki getVideoInfo() değerini kullanarak geçerli VideoInfo örneğini alın. Bu örnek, HDR TV biçiminin türünü ve ekran yüksekliğini ve genişliğini piksel cinsinden içerir. 4K biçiminin varyantları sabit değerlerle HDR_TYPE_* gösterilir.

Birden fazla cihaza uzaktan kumanda ile bildirim gönderme

Bir kullanıcı yayın yaparken aynı ağdaki diğer Android cihazlar da oynatmayı kontrol etmesine olanak tanıyan bir bildirim alır. Cihazı bu tür bildirimleri alan herkes, Google > Google Cast'teki Ayarlar uygulaması > Uzaktan kumanda bildirimlerini göster'e giderek söz konusu cihaz için bildirimleri kapatabilir. (Bildirimlerde, Ayarlar uygulamasının bir kısayolu bulunur.) Daha fazla bilgi için Uzaktan kumanda bildirimlerini yayınlama bölümüne bakın.

Mini kumanda ekleyin

Yayın Tasarımı Kontrol Listesi'ne göre, gönderen uygulama, mini denetleyici olarak bilinen kalıcı bir denetim sağlamalıdır. Bu kontrol, kullanıcı mevcut içerik sayfasından ayrılıp gönderen uygulamanın başka bir bölümüne gittiğinde görünür. Mini kumanda, kullanıcıya geçerli Yayın oturumuyla ilgili görünür bir hatırlatıcı sunar. Kullanıcılar, mini kumandaya dokunarak tam ekran yayın genişletilmiş kumanda görünümüne geri dönebilir.

Çerçeve, mini denetleyiciyi göstermek istediğiniz her etkinliğin düzen dosyasının en altına ekleyebileceğiniz özel bir Görünüm ( 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 video veya işitsel canlı yayın oynatırken SDK, mini kumandadaki oynat/duraklat düğmesinin yerine otomatik olarak bir oynat/durdur düğmesi görüntüler.

Bu özel görünümün başlık ve alt başlığının metin görünümünü ayarlamak ve düğmeleri seçmek için Mini Kumandayı Özelleştirme bölümüne bakın.

Genişletilmiş denetleyici ekle

Google Cast Tasarım Kontrol Listesi, bir gönderen uygulamanın Yayınlamakta olan medya için genişletilmiş bir denetleyici sağlamasını gerektirir. Genişletilmiş kumanda, mini kumandanın tam ekran sürümüdür.

Cast SDK'sı, genişletilmiş kumanda için ExpandedControllerActivity adlı bir widget sağlar. Bu, bir Yayınla düğmesi eklemek için alt sınıfa geçmeniz gereken soyut bir sınıftır.

İlk olarak, genişletilmiş denetleyicinin Yayınla düğmesini sağlaması için yeni bir menü kaynak 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 aralığını kapsayan 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 etkinliklerinizi uygulama manifest dosyasında application etiketinin içinde 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 özelliğini düzenleyip NotificationOptions ile CastMediaOptions öğelerini değiştirerek hedef aktiviteyi yeni etkinliğinize ayarlayın:

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örüntülemek 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 video veya işitsel canlı yayın oynatırken SDK, genişletilmiş kumandadaki oynat/duraklat düğmesinin yerine otomatik olarak bir oynat/durdur düğmesi görüntüler.

Görünümü temaları kullanarak ayarlamak için hangi düğmelerin gösterileceğini seçin ve özel düğmeler ekleyin. Genişletilmiş Denetleyiciyi Özelleştirme bölümüne bakın.

Ses düzeyi kontrolü

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

Fiziksel düğme ses seviyesi kontrolü

Android'de gönderen cihazın üzerindeki fiziksel düğmeler, Jelly Bean veya sonraki sürümleri kullanan herhangi bir cihaz için varsayılan olarak Web Alıcısı'ndaki Yayın oturumunun ses düzeyini değiştirmek amacıyla kullanılabilir.

Jelly Bean'den önceki fiziksel düğme ses seviyesi kontrolü

Jelly Bean'den eski Android cihazlarda Web Alıcısı cihazın ses düzeyini kontrol etmek üzere fiziksel ses seviyesi tuşlarını kullanmak için, gönderen uygulama, Etkinlikleri'nde dispatchKeyEvent değerini geçersiz kılmalı ve CastContext.onDispatchVolumeKeyEventBeforeJellyBean()'ı ç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);
    }
}

Bildirime ve kilit ekranına medya denetimleri ekleme

Yalnızca Android'de Google Cast Tasarım Kontrol Listesi, gönderen uygulamasının medya kontrollerini bir bildirime ve gönderenin yayın yaptığı ancak gönderenin uygulamanın odaklanmadığı kilit ekranında uygulamasını gerektirir. Çerçeve, gönderen uygulamanın bir bildirimde ve kilit ekranında medya kontrolleri oluşturmasına yardımcı olmak için MediaNotificationService ve MediaIntentReceiver özelliklerini sağlar.

MediaNotificationService, gönderen yayındayken çalıştırılır ve geçerli yayın öğesi, oynat/duraklat düğmesi ve durdur düğmesi ile ilgili küçük resim ve bilgiler içeren bir bildirim gösterir.

MediaIntentReceiver, bildirimdeki kullanıcı işlemlerini işleyen bir BroadcastReceiver öğesidir.

Uygulamanız, kilit ekranından bildirim ve medya kontrolünü NotificationOptions aracılığıyla yapılandırabilir. Uygulamanız, bildirimde hangi kontrol düğmelerinin gösterileceğini ve kullanıcı bildirime dokunduğunda hangi Activity cihazının 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ından medya denetimlerinin gösterilmesi varsayılan olarak etkindir ve CastMediaOptions.Builder içinde null ile setNotificationOptions çağrısı yapılarak devre dışı bırakılabilir. Şu anda, bildirim özelliği etkin 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 video veya işitsel canlı yayın oynatırken SDK, bildirim kontrolündeki oynat/duraklat düğmesinin yerine otomatik olarak bir oynat/durdur düğmesi gösterir, ancak kilit ekranı kontrolünde göstermez.

Not: Lollipop öncesi cihazlarda kilit ekranı denetimlerini görüntülemek için RemoteMediaClient, sizin adınıza otomatik olarak ses odağı isteğinde bulunur.

Hataları işleme

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