Cast'i Android Uygulamanıza Entegre Edin

Bu geliştirici kılavuzunda, Android Gönderen SDK'sını kullanarak Android gönderen uygulamanıza Google Cast desteğinin nasıl ekleneceği 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, Cast sınıfı kitaplık ikili programını ve gönderende çalışma zamanında mevcut ilişkili kaynakları belirtir. Gönderen uygulaması veya Yayın uygulaması, gönderen üzerinde de çalışan bir uygulamayı ifade eder. Web Alıcısı uygulaması, Cast uyumlu cihazda çalışan HTML uygulamasını belirtir.

Gönderen çerçevesi, gönderenleri etkinlikler konusunda bilgilendirmek ve Cast uygulama 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, gönderenin Android uygulaması için genel üst düzey yürütme akışı açıklanmaktadır:

  • Yayın ç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, Yayınlama iletişim kutusunu keşfedilen Yayın cihazlarının listesiyle sunar.
  • 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 uygulamasında geri çağırmaları çağırır.
  • Bu çerçeve, gönderen ile Web Alıcısı uygulamaları arasında bir iletişim kanalı oluşturur.
  • Çerçeve, Web Alıcısı'nda medya oynatmasını yüklemek ve kontrol etmek için iletişim kanalını kullanır.
  • Çerçeve, gönderen ile Web Alıcısı'nın medya oynatma durumunu senkronize eder: Kullanıcı, gönderen kullanıcı arayüzü işlemlerini yaptığında, çerçeve bu medya kontrolü isteklerini Web Alıcı'ya iletir ve Web Alıcısı, medya durumu güncellemelerini gönderdiğinde çerçeve de gönderen kullanıcı arayüzünün durumunu günceller.
  • Kullanıcı, Yayın cihazıyla olan bağlantısını kesmek için Yayın 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 listesini Android için Google Cast Gönderen API'si Referansı'nda bulabilirsiniz. Aşağıdaki bölümlerde, Google Cast'i Android uygulamanıza eklemek için uygulanması gereken adımlar açıklanmaktadır.

Android manifest'ini yapılandırın

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

kullanımlar-sdk

Cast SDK'sının desteklediği minimum ve Android API düzeylerini hedefleyin. Şu anda minimum API düzeyi 21 ve hedef API düzeyi 28'dir.

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

android:tema

Uygulamanızın temasını minimum Android SDK sürümüne göre belirleyin. Örneğin, kendi temanızı uygulamıyorsanız Lollipop öncesi minimum Android SDK sürümünü hedeflerken Theme.AppCompat değişkenini 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ı İlk Kullanıma Hazırlama

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

CastContext tek programını başlatmak için gereken seçenekleri sağlamak üzere uygulamanız OptionsProvider arayüzünü uygulamalıdır. OptionsProvider, çerçevenin davranışını etkileyen seçenekleri içeren bir CastOptions örneği sağlar. Bunlar arasından 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ı 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 adını, gönderen uygulamanın AndroidManifest.xml dosyasında meta veri alanı olarak bildirmeniz 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);
    }
}

Cast Kullanıcı Deneyimi Widget'ları

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

  • Tanıtım Yer Paylaşımı: Çerçeve, bir alıcı ilk kez sunulduğunda Yayınla düğmesine dikkat etmesi gereken kullanıcıya gösterilen özel bir Görünüm (IntroductoryOverlay) sağlar. Gönderen uygulaması başlık metninin metnini ve konumunu özelleştirebilir.

  • Yayın Düğmesi: Bir alıcı, uygulamanızı destekleyen bir uygulama keşfedildiğinde Yayınla düğmesi görünür. Kullanıcı, Yayınla düğmesini ilk kez tıkladığında, bulunan cihazları listeleyen bir Yayın iletişim kutusu görüntülenir. Kullanıcı, cihaz bağlıyken Yayınla düğmesini tıkladığında başlık, kayıt stüdyosu adı ve küçük resim gibi geçerli medya meta verilerini gösterir veya kullanıcının Yayın cihazıyla bağlantısını kesmesine izin verir.

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

  • Genişletilmiş Denetleyici: Kullanıcı içerik yayınlarken medya bildirimini veya mini kumandayı tıklarsa genişletilmiş denetleyici açılır ve şu anda oynatılan medya meta verilerini gösterir ve medya oynatmayı kontrol etmek için birkaç düğme sağlar.

  • Bildirim: Yalnızca Android. Kullanıcı içerik yayınladığında ve gönderen uygulamasından ayrıldığında, o anda 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) o anda yayın yapan 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ı uygulamanıza nasıl ekleyeceğinizle ilgili açıklamalar yer almaktadır.

Yayın düğmesi ekle

Android MediaRouter API'leri, ikincil cihazlarda medya görüntülemeyi ve oynatmayı etkinleştirmek için tasarlanmıştır. MediaRouter API kullanan Android uygulamaları, kullanıcıların Yayın cihazı gibi ikincil bir cihazda medya oynatmak için medya yolu seçmesine olanak tanımak amacıyla kullanıcı arayüzünün bir parçasında Yayın düğmesi içermelidir.

Çerçeve, MediaRouteButton'i Cast button olarak eklemeyi oldukça kolaylaştırır. İlk olarak, menünüzü tanımlayan xml dosyasına bir menü öğesi veya bir MediaRouteButton eklemeniz ve bunu çerçeveye bağlamak için CastButtonFactory öğesini 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 öğeniz FragmentActivity mülkünden devralını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ü bir tema kullanarak ayarlamak için Yayın Düğmesini Özelleştir konusuna bakın.

Cihaz keşfini yapılandırma

Cihaz keşfi tamamen CastContext tarafından yönetilir. CastContext'i başlatırken, gönderen uygulama Web Alıcısı uygulama kimliğini belirtir ve isteğe bağlı olarak, CastOptions'te supportedNamespaces'yi ayarlayarak ad alanı filtrelemesi isteyebilir. CastContext, dahili olarak MediaRouter referansı içerir ve gönderen uygulaması ön plana girdiğinde keşif sürecini başlatır ve gönderen uygulama arka plana geçtiğinde durur.

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

Google Cast SDK'sı, bir Cihaza bağlanma, bir Web Alıcısı uygulaması başlatma (ya da birleştirme), bu uygulamaya bağlanma ve medya kontrol kanalı başlatma adımlarını birleştiren Yayın oturumu kavramını tanımlar. Yayınlama oturumları ve Web Alıcı yaşam döngüsü hakkında daha fazla bilgi için Web Alıcısı'na Uygulama yaşam döngüsü kılavuzuna bakın.

Oturumlar, uygulamanızın CastContext.getSessionManager() üzerinden erişebileceği SessionManager sınıfı tarafından yönetilir. Her oturum, 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 etkinken otomatik olarak anormal/ani sonlandırma şeklinde devam eder.

Oturumlar, MediaRouter iletişim kutularından kullanıcı hareketlerine göre otomatik olarak oluşturulur ve silinir.

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

Oturumun durum değişikliklerinden haberdar olmanız gerekirse bir SessionManagerListener uygulayabilirsiniz. Bu örnekte, bir Activity'nin CastSession kullanılabilirliği gösterilmektedir.

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ış aktarma

Oturum durumunu korumak, kullanıcıların ses komutlarını, Google Home uygulamasını veya akıllı ekranları kullanarak mevcut ses ve video akışlarını cihazlar arasında taşıyabileceği akış aktarımının temelini oluşturur. Medya, bir cihazda (kaynak) oynamayı durdurur ve başka bir cihazda (hedef) devam eder. En son donanım yazılımına sahip tüm Yayın cihazları, akış aktarımında kaynak veya hedef işlevi görebilir.

Akış aktarma veya genişletme işlemi sırasında yeni hedef cihazı almak için CastSession#addCastListener kullanarak Cast.Listener kaydedin. Ardından, onDeviceNameChanged geri çağırması sırasında CastSession#getCastDevice() numaralı telefonu arayın.

Daha fazla bilgi için Web Alıcısı üzerinden akış aktarma bölümüne göz atın.

Otomatik yeniden bağlanma

Çerçeve bir ReconnectionService sağlar. Gönderen uygulama, bağlantıyı yeniden başlatmak için bu uygulamayı etkinleştirebilir. Örneğin:

  • Geçici kablosuz ağ bağlantısı kaybından kurtulma
  • Cihaz uykusundan kurtar
  • Uygulamayı arka planda kurtarın
  • Uygulama kilitlendiyse kurtarma

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

Gradle dosyanızda otomatik birleştirme etkinleştirilmişse bu hizmet otomatik olarak uygulamanızın manifest dosyasında birleştirilebilir.

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

Medya Kontrolü'nün işleyiş şekli

Yayın çerçevesi, RemoteMediaPlayer sınıfını Cast 2.x sürümünden kaldıracak şekilde, yeni bir sınıf yerine RemoteMediaClient kullanıma sundu. Bu API, aynı işlevi daha rahat API'ler içerisinde sunan ve GoogleApiClient iletimi yapmaktan kaçınan bir sınıf.

Uygulamanız, medya ad alanını destekleyen bir Web Alıcısı uygulaması ile CastSession kurduğunda çerçeve tarafından otomatik olarak bir 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, bu isteği izlemek için kullanılabilecek bir pendingResult nesnesi döndürür.

RemoteMediaClient örneğinin, uygulamanızın birden fazla parçası ve hatta kalıcı mini kumandalar ve bildirim hizmeti gibi çerçevenin bazı dahili bileşenleri tarafından paylaşılması beklenir. Bu amaçla, bu örnek birden fazla RemoteMediaClient.Listener kaydının kaydedilmesini destekler.

Medya meta verilerini ayarlama

MediaMetadata sınıfı, yayınlamak istediğiniz medya öğesi hakkındaki bilgileri temsil eder. Aşağıdaki örnek, bir filmin yeni MediaMetale örneğini oluşturur ve başlığı, alt başlığı ve iki görüntüyü 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 verileriyle resim kullanımı hakkında Resim Seçimi bölümüne göz atın.

Medya yükle

Uygulamanız, aşağıdaki kodda gösterildiği gibi bir medya öğesi yükleyebilir. İlk olarak, MediaInfo örneğini oluşturmak için medyanın meta verileriyle MediaInfo.Builder kullanın. Mevcut CastSession öğesinden RemoteMediaClient öğesini alın, ardından MediaInfo öğesini bu RemoteMediaClient öğesine yükleyin. Medya Alıcısı'nda çalışan bir medya oynatıcı uygulamasını oynatmak, duraklatmak ve başka şekilde kontrol etmek için RemoteMediaClient 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 ile ilgili bölüme bakın.

4K video biçimi

Medyanızın hangi video biçimini kontrol ettiğini öğrenmek için MediaStatus'da getVideoInfo() kullanarak mevcut VideoInfo örneğini alın. Bu örnekte HDR TV biçiminin türü ile ekran yüksekliği ve genişliğinin piksel cinsinden ifadesi bulunmaktadır. 4K biçimindeki varyantlar sabit sayılarla HDR_TYPE_* gösterilir.

Birden fazla cihaza uzaktan kumanda bildirimleri

Bir kullanıcı içerik yayınlarken aynı ağdaki diğer Android cihazlar da oynatmayı kontrol etmeleri için bildirim alır. Cihazı bu tür bildirimleri alan herkes, Google'da Ayarlar uygulaması > Google Cast > Uzaktan kumanda bildirimlerini göster bölümünden bu cihaz için bildirimleri kapatabilir. (Bildirimlerde, Ayarlar uygulamasının bir kısayolu bulunur.) Daha ayrıntılı bilgi için Uzaktan kumandayı yayınlama bildirimleri bölümüne bakın.

Mini kumanda ekle

Yayın Tasarımı Kontrol Listesi'ne göre, bir gönderen uygulaması, kullanıcı mevcut içerik sayfasından ayrıldığında, gönderenin uygulamasının başka bir bölümüne gittiğinde mini denetleyici olarak bilinen kalıcı bir kontrol sağlamalıdır. Mini kumanda, geçerli yayın oturumunun kullanıcısına görünür bir hatırlatıcı sağlar. Kullanıcı, mini kumandaya dokunarak Yayın tam ekran genişletilmiş kumanda görünümüne geri dönebilir.

Çerçeve, özel bir Görünüm, MiniControllerFragment sunar. Bu denetleyiciyi, mini denetleyiciyi göstermek istediğiniz her etkinliğin düzen dosyasının en altına ekleyebilirsiniz.

<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 kumandadaki oynat/duraklat düğmesinin yerine otomatik olarak bir oynat/dur düğmesi gösterir.

Bu özel görünümün başlığının ve alt metninin metin görünümünü ayarlamak ve düğmeleri seçmek için Mini Denetleyiciyi Özelleştir'e bakın.

Genişletilmiş kumanda ekle

Google Cast Tasarım Kontrol Listesi için bir gönderen uygulamasının, yayınlanacak medya için genişletilmiş kumanda sağlaması gerekir. Genişletilmiş kumanda, mini kumandanın tam ekran sürümüdür.

Cast SDK'sı, ExpandedControllerActivity adlı genişletilmiş kumanda için 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ş kumanda için Yayınla düğmesini sağlamak üzere 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'ı 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 application etiketi içindeki uygulama manifest dosyasında yeni etkinliğinizi tanımlayın:

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

Hedef etkinliği yeni etkinliğinize ayarlamak için CastOptionsProvider öğesini düzenleyin ve NotificationOptions ve CastMediaOptions ayarlarını 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ö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 bir video veya ses canlı yayını oynatırken SDK, genişletilmiş kumandadaki oynatma/duraklatma düğmesinin yerine otomatik olarak bir oynat/dur düğmesi gösterir.

Temaları kullanarak görünümü ayarlamak, hangi düğmelerin gösterileceğini seçmek ve özel düğmeler eklemek için Genişletilmiş Denetleyiciyi Özelleştirme konusuna 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ı her zaman Web Alıcısı tarafından belirtilen hacmi bildirir.

Fiziksel düğme ses denetimi

Android'de, Jelly Bean veya daha yeni bir sürümün yüklü olduğu tüm cihazlarda, web alıcısındaki Yayın oturumunun ses düzeyini varsayılan olarak değiştirmek için, gönderen cihazındaki fiziksel düğmeler kullanılabilir.

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

Jelly Bean'den daha eski Android cihazlarda Web Alıcısı cihaz hacmini kontrol etmek amacıyla fiziksel ses seviyesi tuşlarını kullanmak için, gönderen uygulamanın Etkinliklerindeki dispatchKeyEvent ayarını geçersiz kılması ve CastContext.onDispatchVolumeKeyEventBeforeJellyBean() yöntemini çağırması gerekir:

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

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

MediaNotificationService, gönderen içeriği yayınlarken çalışır ve mevcut küçük resimle ilgili bilgilerin yanı sıra oynatma/duraklatma düğmesi ve durdur düğmesi ile ilgili bilgilerin yer aldığı bir bildirim gösterir.

MediaIntentReceiver, kullanıcı bildirimlerini bildirimden işleyen BroadcastReceiver öğesidir.

Uygulamanız, kilit ekranından NotificationOptions tarihine kadar bildirim ve medya kontrolü yapılandırabilir. Uygulamanız, bildirimde hangi kontrol düğmelerinin gösterileceğini ve bildirime kullanıcı dokunduğunda hangi Activity düğmelerinin açılacağını yapılandırabilir. İşlemler açık bir şekilde 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 denetimleri gösterme, varsayılan olarak etkindir ve CastMediaOptions.Builder içinde boş setNotificationOptions çağrısı yaparak devre dışı bırakılabilir. Şu anda bildirim etkin olduğu sürece kilit ekranı özelliği etkin durumdadı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ünde oynatma/duraklatma düğmesinin yerine otomatik olarak bir oynat/dur düğmesi gösterir, ancak kilit ekranı kontrolünde göstermez.

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

Hataları işleme

Gönderen uygulamaların tüm hata geri çağırmalarını ele alması ve yayınlama yaşam döngüsünün her aşaması için en iyi yanıtı seçmesi çok önemlidir. Uygulama, kullanıcıya hata iletişim kutuları gösterebilir veya Web Alıcısı ile olan bağlantıyı yıkmaya karar verebilir.