ตัวสลับเอาต์พุตเป็นฟีเจอร์ของ Cast SDK ที่ช่วยให้โอนข้อมูลได้อย่างราบรื่น
ระหว่างการเล่นเนื้อหาในเครื่องและจากระยะไกลโดยเริ่มจาก Android 13 เป้าหมาย
คือช่วยให้แอปของผู้ส่งควบคุมเนื้อหาที่เล่นได้อย่างรวดเร็วและง่ายดาย
ตัวสลับเอาต์พุตใช้
คลัง MediaRouter
ไปยัง
เปลี่ยนการเล่นเนื้อหาระหว่างลำโพงโทรศัพท์ อุปกรณ์บลูทูธที่จับคู่ไว้
และอุปกรณ์ที่พร้อมใช้งาน Cast ระยะไกล กรณีการใช้งานแบ่งออกเป็นรายการต่อไปนี้
สถานการณ์ต่างๆ
ดาวน์โหลดและใช้แอปตัวอย่าง CastVideos-android เพื่อดูวิธีใช้งานตัวสลับเอาต์พุตในแอปของคุณ
ควรเปิดใช้ตัวสลับเอาต์พุตเพื่อรองรับการเชื่อมต่อจากในตัวเครื่องสู่ระยะไกล และระยะไกลไปยังเครื่อง และระยะไกลสู่รีโมตโดยทําตามขั้นตอนที่อธิบายไว้ในคู่มือนี้ ไม่มี ขั้นตอนเพิ่มเติมที่จำเป็นเพื่อรองรับการโอนระหว่างอุปกรณ์ในเครือข่ายเดียวกัน ลำโพงและอุปกรณ์บลูทูธที่จับคู่ไว้
UI ของตัวสลับเอาต์พุต
ตัวสลับเอาต์พุตจะแสดงอุปกรณ์เฉพาะที่และระยะไกลที่ใช้ได้ รวมทั้งสถานะปัจจุบันของอุปกรณ์ รวมถึงหากมีการเลือกอุปกรณ์ไว้ กำลังเชื่อมต่อ หรือระดับเสียงปัจจุบัน หากมีอุปกรณ์อื่นๆ นอกเหนือจากนี้ ไปยังอุปกรณ์ปัจจุบัน การคลิกอุปกรณ์อื่นจะช่วยให้คุณโอนสื่อได้ เล่นไปยังอุปกรณ์ที่เลือก
ปัญหาที่ทราบ
- ระบบจะปิดเซสชันสื่อที่สร้างขึ้นสำหรับการเล่นภายในเครื่องและสร้างขึ้นใหม่ เมื่อเปลี่ยนไปใช้การแจ้งเตือน Cast SDK
จุดแรกเข้า
การแจ้งเตือนสื่อ
หากแอปโพสต์การแจ้งเตือนสื่อที่มี
MediaSession
สำหรับ
การเล่นในเครื่อง (เล่นในเครื่อง) ที่มุมขวาบนของการแจ้งเตือนสื่อ
จะแสดงชิปการแจ้งเตือนพร้อมชื่ออุปกรณ์ (เช่น ลำโพงโทรศัพท์) ซึ่ง
ที่กำลังเล่นเนื้อหานี้อยู่ การแตะชิปการแจ้งเตือนจะเปิดขึ้น
UI ของระบบกล่องโต้ตอบตัวสลับเอาต์พุต
การตั้งค่าระดับเสียง
ยังสามารถเรียกใช้ UI ระบบกล่องโต้ตอบตัวสลับเอาต์พุตได้โดยคลิก ปุ่มปรับระดับเสียงบนอุปกรณ์ โดยแตะไอคอนการตั้งค่าที่ด้านล่าง และแตะปุ่ม "เปิด <ชื่อแอป> บน <อุปกรณ์แคสต์>" ข้อความ
สรุปขั้นตอน
- ตรวจสอบว่าเป็นไปตามข้อกำหนดเบื้องต้น
- เปิดใช้ตัวสลับเอาต์พุตใน AndroidManifest.xml
- อัปเดต SessionManagerListener สำหรับการแคสต์ในเบื้องหลัง
- เพิ่มการรองรับสำหรับรีโมตสู่รีโมต
- ตั้งค่าแฟล็ก setRemoteToLocalEnabled
- เล่นในเครื่องต่อ
ข้อกำหนดเบื้องต้น
- ย้ายข้อมูลแอป Android ที่มีอยู่ไปยัง AndroidX
- อัปเดต
build.gradle
ของแอปให้ใช้เวอร์ชันต่ำสุดที่กำหนด Android Sender SDK สำหรับตัวสลับเอาต์พุต:dependencies { ... implementation 'com.google.android.gms:play-services-cast-framework:21.2.0' ... }
- แอปรองรับการแจ้งเตือนสื่อ
- อุปกรณ์ที่ใช้ Android 13
ตั้งค่าการแจ้งเตือนสื่อ
หากต้องการใช้ตัวสลับเอาต์พุต
เสียงและ
แอปวิดีโอ
เพื่อสร้างการแจ้งเตือนสื่อเพื่อแสดงสถานะการเล่นและ
สำหรับสื่อสำหรับการเล่นในเครื่อง ซึ่งจำเป็นต้องสร้าง
MediaSession
การตั้งค่า
MediaStyle
ด้วยโทเค็นของ MediaSession
และตั้งค่าตัวควบคุมสื่อใน
การแจ้งเตือน
หากคุณไม่ได้ใช้ MediaStyle
และ MediaSession
อยู่ ข้อมูลโค้ด
ด้านล่างนี้แสดงวิธีตั้งค่าและคู่มือการตั้งค่าสื่อ
Callback ของเซสชันสำหรับ
เสียงและ
วิดีโอ
แอปพลิเคชัน:
// Create a media session. NotificationCompat.MediaStyle // PlayerService is your own Service or Activity responsible for media playback. val mediaSession = MediaSessionCompat(this, "PlayerService") // Create a MediaStyle object and supply your media session token to it. val mediaStyle = Notification.MediaStyle().setMediaSession(mediaSession.sessionToken) // Create a Notification which is styled by your MediaStyle object. // This connects your media session to the media controls. // Don't forget to include a small icon. val notification = Notification.Builder(this@PlayerService, CHANNEL_ID) .setStyle(mediaStyle) .setSmallIcon(R.drawable.ic_app_logo) .build() // Specify any actions which your users can perform, such as pausing and skipping to the next track. val pauseAction: Notification.Action = Notification.Action.Builder( pauseIcon, "Pause", pauseIntent ).build() notification.addAction(pauseAction)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { // Create a media session. NotificationCompat.MediaStyle // PlayerService is your own Service or Activity responsible for media playback. MediaSession mediaSession = new MediaSession(this, "PlayerService"); // Create a MediaStyle object and supply your media session token to it. Notification.MediaStyle mediaStyle = new Notification.MediaStyle().setMediaSession(mediaSession.getSessionToken()); // Specify any actions which your users can perform, such as pausing and skipping to the next track. Notification.Action pauseAction = Notification.Action.Builder(pauseIcon, "Pause", pauseIntent).build(); // Create a Notification which is styled by your MediaStyle object. // This connects your media session to the media controls. // Don't forget to include a small icon. String CHANNEL_ID = "CHANNEL_ID"; Notification notification = new Notification.Builder(this, CHANNEL_ID) .setStyle(mediaStyle) .setSmallIcon(R.drawable.ic_app_logo) .addAction(pauseAction) .build(); }
นอกจากนี้ หากต้องการป้อนข้อมูลในการแจ้งเตือนด้วยข้อมูลสำหรับสื่อของคุณ
คุณจะต้องเพิ่มแท็ก
ข้อมูลเมตาและสถานะการเล่น
เป็น MediaSession
หากต้องการเพิ่มข้อมูลเมตาลงใน MediaSession
ให้ใช้
setMetaData()
และแสดงข้อมูลทั้งหมดที่เกี่ยวข้อง
ค่าคงที่ MediaMetadata
สำหรับ
ใน
MediaMetadataCompat.Builder()
mediaSession.setMetadata(MediaMetadataCompat.Builder() // Title .putString(MediaMetadata.METADATA_KEY_TITLE, currentTrack.title) // Artist // Could also be the channel name or TV series. .putString(MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist) // Album art // Could also be a screenshot or hero image for video content // The URI scheme needs to be "content", "file", or "android.resource". .putString( MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri) ) // Duration // If duration isn't set, such as for live broadcasts, then the progress // indicator won't be shown on the seekbar. .putLong(MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration) .build() )
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { mediaSession.setMetadata( new MediaMetadataCompat.Builder() // Title .putString(MediaMetadata.METADATA_KEY_TITLE, currentTrack.title) // Artist // Could also be the channel name or TV series. .putString(MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist) // Album art // Could also be a screenshot or hero image for video content // The URI scheme needs to be "content", "file", or "android.resource". .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri) // Duration // If duration isn't set, such as for live broadcasts, then the progress // indicator won't be shown on the seekbar. .putLong(MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration) .build() ); }
หากต้องการเพิ่มสถานะการเล่นใน MediaSession
ให้ใช้
setPlaybackState()
และแสดงข้อมูลทั้งหมดที่เกี่ยวข้อง
PlaybackStateCompat
ค่าคงที่สำหรับสื่อของคุณใน
PlaybackStateCompat.Builder()
mediaSession.setPlaybackState( PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, // Playback position // Used to update the elapsed time and the progress bar. mediaPlayer.currentPosition.toLong(), // Playback speed // Determines the rate at which the elapsed time changes. playbackSpeed ) // isSeekable // Adding the SEEK_TO action indicates that seeking is supported // and makes the seekbar position marker draggable. If this is not // supplied seek will be disabled but progress will still be shown. .setActions(PlaybackStateCompat.ACTION_SEEK_TO) .build() )
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { mediaSession.setPlaybackState( new PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, // Playback position // Used to update the elapsed time and the progress bar. mediaPlayer.currentPosition.toLong(), // Playback speed // Determines the rate at which the elapsed time changes. playbackSpeed ) // isSeekable // Adding the SEEK_TO action indicates that seeking is supported // and makes the seekbar position marker draggable. If this is not // supplied seek will be disabled but progress will still be shown. .setActions(PlaybackStateCompat.ACTION_SEEK_TO) .build() ); }
ลักษณะการทำงานของการแจ้งเตือนแอปวิดีโอ
แอปวิดีโอหรือแอปเสียงที่ไม่รองรับการเล่นในเครื่องขณะล็อกหน้าจอหรือขณะใช้แอปอื่น ควรใช้ลักษณะการทำงานเฉพาะสำหรับการแจ้งเตือนสื่อเพื่อหลีกเลี่ยงปัญหาเกี่ยวกับ การส่งคำสั่งสื่อในกรณีที่ไม่สนับสนุนการเล่น
- โพสต์การแจ้งเตือนสื่อเมื่อเปิดสื่อในเครื่องและแอปอยู่ในนั้น เบื้องหน้า
- หยุดเล่นในเครื่องชั่วคราวและปิดการแจ้งเตือนเมื่อแอปอยู่ใน พื้นหลัง
- เมื่อแอปย้ายกลับไปที่พื้นหน้า การเล่นในเครื่องควรกลับมาทำงานอีกครั้งและ การแจ้งเตือนควรถูกโพสต์อีกครั้ง
เปิดใช้ตัวสลับเอาต์พุตใน AndroidManifest.xml
ในการเปิดใช้ตัวสลับเอาต์พุต พารามิเตอร์
MediaTransferReceiver
จำเป็นต้องเพิ่มลงใน AndroidManifest.xml
ของแอป ถ้าไม่ใช่ คุณลักษณะ
จะไม่เปิดใช้งาน และแฟล็กฟีเจอร์ระยะไกลไปยังในเครื่องก็จะไม่ถูกต้องด้วย
<application>
...
<receiver
android:name="androidx.mediarouter.media.MediaTransferReceiver"
android:exported="true">
</receiver>
...
</application>
MediaTransferReceiver
เป็น Broadcast Receiver ที่ทำให้สามารถโอนสื่อระหว่างอุปกรณ์ต่างๆ ที่มี
UI ดู MediaTransferReceiver
ข้อมูลอ้างอิง
เพื่อดูข้อมูลเพิ่มเติม
จากในตัวไประยะไกล
เมื่อผู้ใช้เปลี่ยนการเล่นจาก "ในเครื่อง" ไปเป็น "ระยะไกล" Cast SDK จะเริ่มต้น
เซสชันการแคสต์โดยอัตโนมัติ อย่างไรก็ตาม แอปจะต้องจัดการกับการเปลี่ยนจาก
ในเครื่องเป็นระยะไกล เช่น หยุดการเล่นในเครื่อง
แล้วโหลดสื่อนั้นในอุปกรณ์แคสต์ แอปควรฟังการแคสต์
SessionManagerListener
โดยใช้
onSessionStarted()
และ
onSessionEnded()
Callback และจัดการการทำงานเมื่อได้รับการแคสต์
SessionManager
Callback แอปควรตรวจสอบว่า Callback เหล่านี้ยังคงใช้งานได้เมื่อ
กล่องโต้ตอบตัวสลับเอาต์พุตเปิดอยู่และแอปไม่ได้ทำงานอยู่เบื้องหน้า
อัปเดต SessionManagerListener สำหรับการแคสต์ในเบื้องหลัง
ประสบการณ์การแคสต์แบบเดิมรองรับ Local-to-remote อยู่แล้วเมื่อแอปทำงาน
อยู่เบื้องหน้า ประสบการณ์การแคสต์โดยทั่วไปจะเริ่มต้นเมื่อผู้ใช้คลิกไอคอน "แคสต์"
ในแอปแล้วเลือกอุปกรณ์ที่จะสตรีมสื่อ ในกรณีนี้ แอปจะต้อง
ลงทะเบียนกับ
SessionManagerListener
ใน onCreate()
หรือ
onStart()
และยกเลิกการลงทะเบียน Listener ใน
onStop()
หรือ
onDestroy()
ของกิจกรรมในแอป
ประสบการณ์การแคสต์แบบใหม่โดยใช้ตัวสลับเอาต์พุตทำให้แอปเริ่ม
แคสต์เมื่ออยู่ในเบื้องหลัง วิธีนี้เป็นประโยชน์อย่างยิ่งสำหรับเสียง
แอปที่โพสต์การแจ้งเตือนเมื่อเล่นอยู่เบื้องหลัง แอปลงทะเบียนได้
SessionManager
Listener ใน onCreate()
ของบริการและยกเลิกการลงทะเบียนใน onDestroy()
ของบริการ แอปควรได้รับ Callback จากต้นทางถึงระยะไกลเสมอ (เช่น
เป็น onSessionStarted
)
เมื่อแอปอยู่ในเบื้องหลัง
หากแอปใช้
MediaBrowserService
ขอแนะนำให้ลงทะเบียน SessionManagerListener
จากที่นั่น
class MyService : Service() { private var castContext: CastContext? = null protected fun onCreate() { castContext = CastContext.getSharedInstance(this) castContext .getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession::class.java) } protected fun onDestroy() { if (castContext != null) { castContext .getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession::class.java) } } }
public class MyService extends Service { private CastContext castContext; @Override protected void onCreate() { castContext = CastContext.getSharedInstance(this); castContext .getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession.class); } @Override protected void onDestroy() { if (castContext != null) { castContext .getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession.class); } } }
การอัปเดตนี้ทำให้ จากท้องถิ่นสู่ระยะไกล ทำงานเหมือนกับการแคสต์แบบทั่วไปเมื่อ แอปอยู่ในเบื้องหลังและไม่จำเป็นต้องดำเนินการเพิ่มเติมเพื่อเปลี่ยนจาก อุปกรณ์บลูทูธกับอุปกรณ์แคสต์
ระยะไกลสู่ท้องถิ่น
ตัวสลับเอาต์พุตทำให้สามารถโอนจากการเล่นระยะไกลไปยัง
ลำโพงโทรศัพท์หรืออุปกรณ์บลูทูธในเครื่อง ซึ่งสามารถเปิดใช้ได้โดยการตั้งค่า
setRemoteToLocalEnabled
แจ้งไปที่ true
ใน CastOptions
สำหรับกรณีที่อุปกรณ์ผู้ส่งปัจจุบันเข้าร่วมเซสชันที่มีอยู่กับ
ผู้ส่งหลายรายและแอปต้องตรวจสอบว่าสื่อปัจจุบันได้รับอนุญาตให้
โอนภายในเครื่องได้ แอปควรใช้onTransferred
Callback ของ SessionTransferCallback
เพื่อตรวจสอบ SessionState
ตั้งค่าแฟล็ก setRemoteToLocalEnabled
CastOptions.Builder
ให้สิทธิ์ setRemoteToLocalEnabled
เพื่อแสดงหรือซ่อนลำโพงโทรศัพท์และอุปกรณ์บลูทูธในพื้นที่เป็น "โอนไป"
เป้าหมายในกล่องโต้ตอบตัวสลับเอาต์พุตเมื่อมีเซสชันการแคสต์ที่ใช้งานอยู่
class CastOptionsProvider : OptionsProvider { fun getCastOptions(context: Context?): CastOptions { ... return Builder() ... .setRemoteToLocalEnabled(true) .build() } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { ... return new CastOptions.Builder() ... .setRemoteToLocalEnabled(true) .build() } }
เล่นในเครื่องต่อ
แอปที่รองรับระยะไกลสู่ท้องถิ่นควรลงทะเบียน SessionTransferCallback
เพื่อรับการแจ้งเตือนเมื่อเกิดเหตุการณ์ขึ้น เพื่อให้พวกเขาตรวจสอบได้ว่าสื่อควร
ได้รับอนุญาตให้โอนและเล่นต่อในเครื่องได้
CastContext#addSessionTransferCallback(SessionTransferCallback)
อนุญาตให้แอปลงทะเบียน SessionTransferCallback
และฟังการเรียกกลับ onTransferred
และ onTransferFailed
เมื่อผู้ส่ง
โอนไปยังการเล่นในเครื่องแล้ว
หลังจากแอปยกเลิกการลงทะเบียน SessionTransferCallback
แล้ว
แอปจะไม่ได้รับ SessionTransferCallback
อีกต่อไป
วิ
SessionTransferCallback
เป็นส่วนขยายของ SessionManagerListener
ที่มีอยู่
Callback และจะทำงานหลังจากมีการเรียกให้แสดง onSessionEnded
แล้ว ลำดับของ
Callback ระยะไกลไปยังภายในคือ
onTransferring
onSessionEnding
onSessionEnded
onTransferred
เนื่องจากตัวสลับเอาต์พุตสามารถเปิดได้ด้วยชิปการแจ้งเตือนสื่อเมื่อ
แอปอยู่ในเบื้องหลังและการแคสต์ แอปต้องจัดการการโอนไปยังเครื่อง
จะแตกต่างออกไป ขึ้นอยู่กับว่ารองรับการเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่นหรือไม่ ในกรณีที่
ของการโอนที่ล้มเหลว onTransferFailed
เริ่มทำงานทุกครั้งที่เกิดข้อผิดพลาด
แอปที่รองรับการเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่น
สำหรับแอปที่รองรับการเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่น (โดยทั่วไปจะเป็นแอปเสียง)
แนะนำให้ใช้ Service
(เช่น MediaBrowserService
) บริการต่างๆ
ควรฟัง onTransferred
โทรกลับและเล่นต่อในเครื่องทั้งที่แอปอยู่เบื้องหน้าหรือ
พื้นหลัง
class MyService : Service() { private var castContext: CastContext? = null private var sessionTransferCallback: SessionTransferCallback? = null protected fun onCreate() { castContext = CastContext.getSharedInstance(this) castContext.getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession::class.java) sessionTransferCallback = MySessionTransferCallback() castContext.addSessionTransferCallback(sessionTransferCallback) } protected fun onDestroy() { if (castContext != null) { castContext.getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession::class.java) if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback) } } } class MySessionTransferCallback : SessionTransferCallback() { fun onTransferring(@SessionTransferCallback.TransferType transferType: Int) { // Perform necessary steps prior to onTransferred } fun onTransferred(@SessionTransferCallback.TransferType transferType: Int, sessionState: SessionState?) { if (transferType == SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } fun onTransferFailed(@SessionTransferCallback.TransferType transferType: Int, @SessionTransferCallback.TransferFailedReason transferFailedReason: Int) { // Handle transfer failure. } } }
public class MyService extends Service { private CastContext castContext; private SessionTransferCallback sessionTransferCallback; @Override protected void onCreate() { castContext = CastContext.getSharedInstance(this); castContext.getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession.class); sessionTransferCallback = new MySessionTransferCallback(); castContext.addSessionTransferCallback(sessionTransferCallback); } @Override protected void onDestroy() { if (castContext != null) { castContext.getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession.class); if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback); } } } public static class MySessionTransferCallback extends SessionTransferCallback { public MySessionTransferCallback() {} @Override public void onTransferring(@SessionTransferCallback.TransferType int transferType) { // Perform necessary steps prior to onTransferred } @Override public void onTransferred(@SessionTransferCallback.TransferType int transferType, SessionState sessionState) { if (transferType==SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } @Override public void onTransferFailed(@SessionTransferCallback.TransferType int transferType, @SessionTransferCallback.TransferFailedReason int transferFailedReason) { // Handle transfer failure. } } }
แอปที่ไม่รองรับการเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่น
สำหรับแอปที่ไม่รองรับการเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่น (โดยทั่วไปจะเป็นแอปวิดีโอ)
แนะนำให้ฟัง onTransferred
โทรกลับและเล่นต่อในเครื่องหากแอปอยู่เบื้องหน้า
หากแอปอยู่ในเบื้องหลัง ควรหยุดเล่นชั่วคราวและจัดเก็บ
ข้อมูลที่จำเป็นจาก SessionState
(เช่น ข้อมูลเมตาของสื่อและตำแหน่งการเล่น) เมื่อแอป
เบื้องหน้าจากเบื้องหลัง การเล่นในเครื่องควรดำเนินการต่อด้วย
ข้อมูลที่จัดเก็บไว้
class MyActivity : AppCompatActivity() { private var castContext: CastContext? = null private var sessionTransferCallback: SessionTransferCallback? = null protected fun onCreate() { castContext = CastContext.getSharedInstance(this) castContext.getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession::class.java) sessionTransferCallback = MySessionTransferCallback() castContext.addSessionTransferCallback(sessionTransferCallback) } protected fun onDestroy() { if (castContext != null) { castContext.getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession::class.java) if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback) } } } class MySessionTransferCallback : SessionTransferCallback() { fun onTransferring(@SessionTransferCallback.TransferType transferType: Int) { // Perform necessary steps prior to onTransferred } fun onTransferred(@SessionTransferCallback.TransferType transferType: Int, sessionState: SessionState?) { if (transferType == SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } fun onTransferFailed(@SessionTransferCallback.TransferType transferType: Int, @SessionTransferCallback.TransferFailedReason transferFailedReason: Int) { // Handle transfer failure. } } }
public class MyActivity extends AppCompatActivity { private CastContext castContext; private SessionTransferCallback sessionTransferCallback; @Override protected void onCreate() { castContext = CastContext.getSharedInstance(this); castContext .getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession.class); sessionTransferCallback = new MySessionTransferCallback(); castContext.addSessionTransferCallback(sessionTransferCallback); } @Override protected void onDestroy() { if (castContext != null) { castContext .getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession.class); if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback); } } } public static class MySessionTransferCallback extends SessionTransferCallback { public MySessionTransferCallback() {} @Override public void onTransferring(@SessionTransferCallback.TransferType int transferType) { // Perform necessary steps prior to onTransferred } @Override public void onTransferred(@SessionTransferCallback.TransferType int transferType, SessionState sessionState) { if (transferType==SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } @Override public void onTransferFailed(@SessionTransferCallback.TransferType int transferType, @SessionTransferCallback.TransferFailedReason int transferFailedReason) { // Handle transfer failure. } } }
รีโมตสู่รีโมต
ตัวสลับเอาต์พุตรองรับการขยายไปยังที่พร้อมใช้งาน Cast หลายรายการ อุปกรณ์ลำโพงสำหรับแอปเสียงที่ใช้การขยายสตรีม
แอปเสียงคือแอปที่รองรับ Google Cast for Audio ในแอปตัวรับ ในการตั้งค่าสำหรับนักพัฒนาซอฟต์แวร์ Google Cast SDK คอนโซล
การขยายสตรีมด้วยลำโพง
แอปเสียงที่ใช้ตัวสลับเอาต์พุตสามารถขยายเสียงได้ ไปยังอุปกรณ์ลำโพงที่พร้อมใช้งาน Cast หลายเครื่องในระหว่างเซสชันการแคสต์โดยใช้สตรีม การขยาย
แพลตฟอร์ม Cast รองรับฟีเจอร์นี้และคุณไม่ต้องดำเนินการใดๆ เพิ่มเติม จะเปลี่ยนแปลงถ้าแอปใช้ UI เริ่มต้น หากใช้ UI ที่กำหนดเอง แอป ควรอัปเดต UI เพื่อให้แสดงว่าแอปกำลังแคสต์ไปยังกลุ่ม
หากต้องการดูชื่อกลุ่มที่ขยายใหม่ในระหว่างการขยายสตรีม
ลงทะเบียน
Cast.Listener
โดยใช้
CastSession#addCastListener
จากนั้นโทร
CastSession#getCastDevice()
ในระหว่าง Callback onDeviceNameChanged
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 val mCastListener = CastListener() private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { addCastListener(session) } override fun onSessionStartFailed(session: CastSession?, error: Int) {} override fun onSessionSuspended(session: CastSession?, reason Int) { removeCastListener() } override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { addCastListener(session) } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { removeCastListener() } } private inner class CastListener : Cast.Listener() { override fun onDeviceNameChanged() { mCastSession?.let { val castDevice = it.castDevice val deviceName = castDevice.friendlyName // Update UIs with the new cast device name. } } } private fun addCastListener(castSession: CastSession) { mCastSession = castSession mCastSession?.addCastListener(mCastListener) } private fun removeCastListener() { mCastSession?.removeCastListener(mCastListener) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onDestroy() { super.onDestroy() mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) } }
public class MyActivity extends Activity { private CastContext mCastContext; private CastSession mCastSession; private SessionManager mSessionManager; private SessionManagerListener<CastSession> mSessionManagerListener = new SessionManagerListenerImpl(); private Cast.Listener mCastListener = new CastListener(); private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { addCastListener(session); } @Override public void onSessionStartFailed(CastSession session, int error) {} @Override public void onSessionSuspended(CastSession session, int reason) { removeCastListener(); } @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { addCastListener(session); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { removeCastListener(); } } private class CastListener extends Cast.Listener { @Override public void onDeviceNameChanged() { if (mCastSession == null) { return; } CastDevice castDevice = mCastSession.getCastDevice(); String deviceName = castDevice.getFriendlyName(); // Update UIs with the new cast device name. } } private void addCastListener(CastSession castSession) { mCastSession = castSession; mCastSession.addCastListener(mCastListener); } private void removeCastListener() { if (mCastSession != null) { mCastSession.removeCastListener(mCastListener); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
การทดสอบจากรีโมตสู่รีโมต
หากต้องการทดสอบฟีเจอร์ ให้ทำดังนี้
- แคสต์เนื้อหาไปยังอุปกรณ์ที่พร้อมใช้งาน Cast โดยใช้การแคสต์แบบทั่วไปหรือด้วย local-to-remote
- เปิดตัวสลับเอาต์พุตโดยใช้จุดแรกเข้า
- แตะอุปกรณ์ที่พร้อมใช้งาน Cast อีกเครื่องหนึ่ง แอปเสียงจะขยายเนื้อหาไปยัง อุปกรณ์เพิ่มเติมและสร้างกลุ่มแบบไดนามิก
- แตะอุปกรณ์ที่พร้อมใช้งาน Cast อีกครั้ง อุปกรณ์ดังกล่าวจะถูกนำออกจากไดนามิก กลุ่ม