เซสชันสื่อเป็นวิธีสากลในการโต้ตอบกับโปรแกรมเล่นเสียงหรือวิดีโอ ใน Media3 โปรแกรมเล่นเริ่มต้นคือคลาส ExoPlayer
ซึ่งใช้อินเทอร์เฟซ Player
การเชื่อมต่อเซสชันสื่อกับโปรแกรมเล่นช่วยให้แอปสามารถโฆษณาการเล่นสื่อภายนอกและรับคำสั่งการเล่นจากแหล่งที่มาภายนอกได้
คำสั่งอาจมาจากปุ่มจริง เช่น ปุ่มเล่นบนหูฟังหรือรีโมตทีวี นอกจากนี้ คำสั่งอาจมาจากแอปไคลเอ็นต์ที่มีตัวควบคุมสื่อ เช่น คำสั่ง "หยุดชั่วคราว" ไปยัง Google Assistant เซสชันสื่อจะมอบสิทธิ์คำสั่งเหล่านี้ให้กับโปรแกรมเล่นของแอปสื่อ
กรณีที่ควรเลือกเซสชันสื่อ
เมื่อใช้ MediaSession
คุณจะอนุญาตให้ผู้ใช้ควบคุมการเล่นได้ดังนี้
- ผ่านหูฟัง โดยมักจะมีปุ่มหรือการโต้ตอบด้วยการสัมผัสที่ผู้ใช้สามารถดำเนินการกับหูฟังเพื่อเล่นหรือหยุดสื่อชั่วคราว หรือไปยังแทร็กถัดไปหรือก่อนหน้า
- โดยพูดกับ Google Assistant รูปแบบทั่วไปคือการพูดว่า "Ok Google หยุดชั่วคราว" เพื่อหยุดสื่อที่เล่นอยู่ในอุปกรณ์ชั่วคราว
- ผ่านนาฬิกา Wear OS วิธีนี้ช่วยให้ผู้ใช้เข้าถึงการควบคุมการเล่นที่ใช้บ่อยที่สุดได้ง่ายขึ้นขณะเล่นบนโทรศัพท์
- ผ่านตัวควบคุมสื่อ ภาพสไลด์นี้จะแสดงตัวควบคุมสำหรับเซสชันสื่อที่เล่นอยู่แต่ละรายการ
- บนทีวี อนุญาตให้ดำเนินการกับปุ่มเล่นจริง การควบคุมการเล่นบนแพลตฟอร์ม และการจัดการพลังงาน (เช่น หากทีวี ซาวด์บาร์ หรือรีซีฟเวอร์ A/V ปิดอยู่หรือมีการสลับอินพุต การเล่นในแอปควรหยุดลง)
- และกระบวนการภายนอกอื่นๆ ที่ต้องส่งผลต่อการเล่น
ซึ่งเหมาะสําหรับ Use Case หลายรายการ โดยเฉพาะอย่างยิ่ง คุณควรพิจารณาใช้ MediaSession
ในกรณีต่อไปนี้
- คุณกำลังสตรีมเนื้อหาวิดีโอแบบยาว เช่น ภาพยนตร์หรือรายการทีวีสด
- คุณกำลังสตรีมเนื้อหาเสียงแบบยาว เช่น พอดแคสต์หรือเพลย์ลิสต์เพลง
- คุณกำลังสร้างแอปทีวี
อย่างไรก็ตาม MediaSession
ไม่ได้เหมาะกับ Use Case บางรายการ คุณอาจต้อง
ใช้เฉพาะ Player
ในกรณีต่อไปนี้
- คุณแสดงเนื้อหาแบบสั้นซึ่งไม่จำเป็นต้องมีการควบคุมจากภายนอกหรือการเล่นในเบื้องหลัง
- ไม่มีวิดีโอที่ใช้งานอยู่เพียงรายการเดียว เช่น ผู้ใช้กำลังเลื่อนดูรายการ และวิดีโอหลายรายการแสดงอยู่บนหน้าจอพร้อมกัน
- คุณกำลังเล่นวิดีโอแนะนำหรืออธิบายแบบครั้งเดียว ซึ่งคุณคาดหวังว่าผู้ใช้จะดูอย่างตั้งใจโดยไม่ต้องใช้ตัวควบคุมการเล่นภายนอก
- เนื้อหาของคุณมีความละเอียดอ่อนด้านความเป็นส่วนตัวและคุณไม่ต้องการให้กระบวนการภายนอกเข้าถึงข้อมูลเมตาของสื่อ (เช่น โหมดไม่ระบุตัวตนในเบราว์เซอร์)
หาก Use Case ของคุณไม่ตรงกับ Use Case ที่ระบุไว้ข้างต้น ให้พิจารณาว่าคุณอนุญาตให้แอปเล่นเนื้อหาต่อไปได้ไหมเมื่อผู้ใช้ไม่ได้มีส่วนร่วมกับเนื้อหา หากคำตอบคือใช่ คุณอาจต้องเลือกตัวเลือก MediaSession
หากคำตอบคือไม่ คุณอาจต้องใช้ Player
แทน
สร้างเซสชันสื่อ
เซสชันสื่อจะแสดงอยู่ควบคู่ไปกับโปรแกรมเล่นที่จัดการ คุณสามารถสร้างเซสชันสื่อด้วยออบเจ็กต์ Context
และ Player
คุณควรสร้างและเริ่มต้นเซสชันสื่อเมื่อจำเป็น เช่น วิธีการเกี่ยวกับวงจรชีวิตของ onStart()
หรือ onResume()
ของ Activity
หรือ Fragment
หรือวิธีการ onCreate()
ของ Service
ที่เป็นเจ้าของเซสชันสื่อและโปรแกรมเล่นที่เชื่อมโยง
หากต้องการสร้างเซสชันสื่อ ให้เริ่มต้น Player
แล้วส่งให้กับ
MediaSession.Builder
ดังนี้
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
การจัดการสถานะอัตโนมัติ
ไลบรารี Media3 จะอัปเดตเซสชันสื่อโดยอัตโนมัติโดยใช้สถานะของเพลเยอร์ คุณจึงไม่ต้องจัดการการแมปจากเพลเยอร์ไปยังเซสชันด้วยตนเอง
ซึ่งแตกต่างจากเซสชันสื่อของแพลตฟอร์มที่คุณต้องสร้างและดูแลPlaybackState
แยกต่างหากจากตัวเพลเยอร์เอง เช่น เพื่อระบุข้อผิดพลาด
รหัสเซสชันที่ไม่ซ้ำกัน
โดยค่าเริ่มต้น MediaSession.Builder
จะสร้างเซสชันโดยใช้สตริงว่างเป็นรหัสเซสชัน ซึ่งเพียงพอแล้วหากแอปตั้งใจจะสร้างอินสแตนซ์เซสชันเดียวเท่านั้น ซึ่งเป็นกรณีที่พบบ่อยที่สุด
หากแอปต้องการจัดการอินสแตนซ์เซสชันหลายรายการพร้อมกัน แอปต้องตรวจสอบว่ารหัสเซสชันของแต่ละเซสชันไม่ซ้ำกัน คุณสามารถตั้งค่ารหัสเซสชันได้เมื่อสร้างเซสชันด้วย MediaSession.Builder.setId(String id)
หากเห็นIllegalStateException
แอปขัดข้องพร้อมข้อความแสดงข้อผิดพลาด IllegalStateException: Session ID must be unique. ID=
แสดงว่าอาจมีการสร้างเซสชันโดยไม่คาดคิดก่อนที่จะมีการปล่อยอินสแตนซ์ที่มีรหัสเดียวกันซึ่งสร้างขึ้นก่อนหน้านี้ ระบบจะตรวจหาและแจ้งเตือนกรณีดังกล่าวด้วยการยกเว้นเพื่อหลีกเลี่ยงไม่ให้เซสชันรั่วไหลเนื่องจากข้อผิดพลาดในการเขียนโปรแกรม
มอบสิทธิ์ควบคุมให้กับไคลเอ็นต์รายอื่น
เซสชันสื่อเป็นกุญแจสำคัญในการควบคุมการเล่น ซึ่งช่วยให้คุณกำหนดเส้นทางคำสั่งจากแหล่งที่มาภายนอกไปยังโปรแกรมเล่นที่ทำหน้าที่เล่นสื่อได้ แหล่งที่มาเหล่านี้อาจเป็นปุ่มจริง เช่น ปุ่มเล่นบนชุดหูฟังหรือรีโมตคอนโทรลทีวี หรือคำสั่งโดยอ้อม เช่น บอกให้ Google Assistant "หยุดชั่วคราว" ในทำนองเดียวกัน คุณอาจต้องให้สิทธิ์เข้าถึงระบบ Android เพื่ออำนวยความสะดวกในการควบคุมการแจ้งเตือนและหน้าจอล็อก หรือให้สิทธิ์เข้าถึงนาฬิกา Wear OS เพื่อให้คุณควบคุมการเล่นจากหน้าปัดได้ ลูกค้าภายนอกสามารถใช้ตัวควบคุมสื่อเพื่อออกคำสั่งการเล่นไปยังแอปสื่อได้ โดยเซสชันสื่อจะรับคำสั่งเหล่านี้และส่งต่อไปยังโปรแกรมเล่นสื่อ

เมื่อตัวควบคุมกำลังจะเชื่อมต่อกับเซสชันสื่อ ระบบจะเรียกใช้เมธอด onConnect()
คุณสามารถใช้ ControllerInfo
ที่ระบุไว้เพื่อตัดสินใจว่าจะยอมรับหรือปฏิเสธคําขอ ดูตัวอย่างการยอมรับคําขอเชื่อมต่อได้ในส่วนประกาศคำสั่ง
หลังจากเชื่อมต่อแล้ว รีโมตคอนโทรลเลอร์จะส่งคำสั่งการเล่นไปยังเซสชันได้ จากนั้นเซสชันจะมอบสิทธิ์คำสั่งเหล่านั้นไปยังโปรแกรมเล่น เซสชันจะจัดการคำสั่งการเล่นและเพลย์ลิสต์ที่กําหนดไว้ในอินเทอร์เฟซ Player
โดยอัตโนมัติ
วิธีการเรียกกลับอื่นๆ ช่วยให้คุณจัดการกับคำขอต่างๆ ได้ เช่น คำขอคําสั่งที่กําหนดเองและการแก้ไขเพลย์ลิสต์ แคล็กแบ็กเหล่านี้จะมีออบเจ็กต์ ControllerInfo
ด้วยเช่นกันเพื่อให้คุณแก้ไขวิธีตอบสนองต่อคำขอแต่ละรายการตามตัวควบคุมแต่ละรายการได้
แก้ไขเพลย์ลิสต์
เซสชันสื่อสามารถแก้ไขเพลย์ลิสต์ของเพลเยอร์ได้โดยตรงตามที่อธิบายไว้ในคู่มือ ExoPlayer สำหรับเพลย์ลิสต์
นอกจากนี้ ผู้ควบคุมยังแก้ไขเพลย์ลิสต์ได้หากมี COMMAND_SET_MEDIA_ITEM
หรือ COMMAND_CHANGE_MEDIA_ITEMS
สำหรับผู้ควบคุม
เมื่อเพิ่มรายการใหม่ลงในเพลย์ลิสต์ โดยทั่วไปแล้วโปรแกรมเล่นจะต้องใช้MediaItem
อินสแตนซ์ที่มี URI ที่กําหนดเพื่อให้เล่นได้ โดยค่าเริ่มต้น ระบบจะส่งต่อรายการที่เพิ่มใหม่โดยอัตโนมัติไปยังเมธอดของโปรแกรมเล่น เช่น player.addMediaItem
หากมีการกําหนด URI
หากต้องการปรับแต่งอินสแตนซ์ MediaItem
ที่เพิ่มลงในเพลเยอร์ คุณสามารถ override
onAddMediaItems()
ขั้นตอนนี้จำเป็นเมื่อคุณต้องการรองรับตัวควบคุมที่ขอสื่อโดยไม่มี URI ที่กําหนดไว้ แต่โดยทั่วไป MediaItem
จะมีการตั้งค่าช่องต่อไปนี้อย่างน้อย 1 ช่องเพื่ออธิบายสื่อที่ขอ
MediaItem.id
: รหัสทั่วไปที่ระบุสื่อMediaItem.RequestMetadata.mediaUri
: URI คำขอที่อาจใช้สคีมาที่กำหนดเองและไม่จำเป็นต้องเล่นได้โดยตรงโดยโปรแกรมเล่นMediaItem.RequestMetadata.searchQuery
: คําค้นหาที่เป็นข้อความ เช่น จาก Google AssistantMediaItem.MediaMetadata
: ข้อมูลเมตาที่มีโครงสร้าง เช่น "ชื่อ" หรือ "ศิลปิน"
หากต้องการตัวเลือกการปรับแต่งเพิ่มเติมสำหรับเพลย์ลิสต์ใหม่ทั้งหมด คุณสามารถลบล้างonSetMediaItems()
เพิ่มเติมได้ ซึ่งจะช่วยให้คุณกำหนดรายการเริ่มต้นและตำแหน่งในเพลย์ลิสต์ได้ ตัวอย่างเช่น คุณสามารถขยายรายการที่ขอรายการเดียวเป็นทั้งเพลย์ลิสต์และสั่งให้โปรแกรมเล่นเริ่มต้นที่ดัชนีของรายการที่ขอในตอนแรก คุณสามารถดูตัวอย่างการใช้งาน onSetMediaItems()
กับฟีเจอร์นี้ได้ในแอปสาธิตเซสชัน
จัดการค่ากำหนดปุ่มสื่อ
ตัวควบคุมทุกตัว เช่น UI ของระบบ, Android Auto หรือ Wear OS จะตัดสินใจได้เองว่าจะแสดงปุ่มใดต่อผู้ใช้ หากต้องการระบุตัวควบคุมการเล่นที่ต้องการแสดงต่อผู้ใช้ ให้ระบุค่ากำหนดปุ่มสื่อใน MediaSession
ค่ากําหนดเหล่านี้ประกอบด้วยรายการอินสแตนซ์ CommandButton
ที่เรียงลําดับ โดยแต่ละรายการจะกําหนดค่ากําหนดสําหรับปุ่มในอินเทอร์เฟซผู้ใช้
กำหนดปุ่มคำสั่ง
อินสแตนซ์ CommandButton
ใช้เพื่อกำหนดค่ากำหนดปุ่มสื่อ ปุ่มแต่ละปุ่มจะกำหนดแง่มุม 3 ด้านขององค์ประกอบ UI ที่ต้องการ ดังนี้
- ไอคอน ซึ่งกำหนดลักษณะที่ปรากฏ คุณต้องตั้งค่าไอคอนเป็นค่าคงที่ที่กําหนดไว้ล่วงหน้าเมื่อสร้าง
CommandButton.Builder
โปรดทราบว่าไฟล์นี้ไม่ใช่ทรัพยากรบิตแมปหรือรูปภาพจริง ค่าคงที่ทั่วไปช่วยให้ตัวควบคุมเลือกทรัพยากรที่เหมาะสมเพื่อให้รูปลักษณ์และความรู้สึกสอดคล้องกันภายใน UI ของตนเองได้ หากไม่มีค่าคงที่ไอคอนที่กําหนดไว้ล่วงหน้าใดที่เหมาะกับกรณีการใช้งานของคุณ คุณก็ใช้setCustomIconResId
แทนได้ - คําสั่ง ซึ่งกําหนดการดําเนินการที่ทริกเกอร์เมื่อผู้ใช้โต้ตอบกับปุ่ม คุณสามารถใช้
setPlayerCommand
สำหรับPlayer.Command
หรือsetSessionCommand
สำหรับSessionCommand
ที่กําหนดไว้ล่วงหน้าหรือกําหนดเอง - ช่อง ซึ่งกำหนดตําแหน่งที่จะวางปุ่มใน UI ของตัวควบคุม ช่องนี้เป็นข้อมูลที่ไม่บังคับและระบบจะตั้งค่าโดยอัตโนมัติตามไอคอนและคําสั่ง เช่น ช่วยให้ระบุได้ว่าปุ่มควรแสดงในพื้นที่การนำทาง "ไปข้างหน้า" ของ UI แทนพื้นที่ "รายการเพิ่มเติม" เริ่มต้น
Kotlin
val button = CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15) .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY)) .setSlots(CommandButton.SLOT_FORWARD) .build()
Java
CommandButton button = new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15) .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY)) .setSlots(CommandButton.SLOT_FORWARD) .build();
เมื่อระบบแก้ไขค่ากำหนดของปุ่มสื่อแล้ว ระบบจะใช้อัลกอริทึมต่อไปนี้
- สําหรับ
CommandButton
แต่ละรายการในค่ากําหนดของปุ่มสื่อ ให้วางปุ่มในช่องแรกที่ใช้ได้และได้รับอนุญาต - หากช่องกลาง ไปข้างหน้า และย้อนกลับไม่มีปุ่ม ให้เพิ่มปุ่มเริ่มต้นสำหรับช่องนี้
คุณสามารถใช้ CommandButton.DisplayConstraints
เพื่อสร้างตัวอย่างวิธีแก้ปัญหาค่ากำหนดปุ่มสื่อโดยขึ้นอยู่กับข้อจำกัดการแสดง UI
ตั้งค่ากำหนดปุ่มสื่อ
วิธีที่ง่ายที่สุดในการตั้งค่ากำหนดของปุ่มสื่อคือการระบุรายการเมื่อสร้าง MediaSession
หรือจะลบล้างค่า MediaSession.Callback.onConnect
เพื่อปรับแต่งค่ากำหนดปุ่มสื่อสำหรับตัวควบคุมที่เชื่อมต่อแต่ละตัวก็ได้
Kotlin
val mediaSession = MediaSession.Builder(context, player) .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton)) .build()
Java
MediaSession mediaSession = new MediaSession.Builder(context, player) .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton)) .build();
อัปเดตค่ากําหนดของปุ่มสื่อหลังจากการโต้ตอบของผู้ใช้
หลังจากจัดการการโต้ตอบกับโปรแกรมเล่นแล้ว คุณอาจต้องอัปเดตปุ่มที่แสดงใน UI ของคอนโทรลเลอร์ ตัวอย่างทั่วไปคือปุ่มเปิด/ปิดที่จะเปลี่ยนไอคอนและการทํางานหลังจากเรียกใช้การดําเนินการที่เกี่ยวข้องกับปุ่มนี้ หากต้องการอัปเดตค่ากำหนดของปุ่มสื่อ ให้ใช้ MediaSession.setMediaButtonPreferences
เพื่ออัปเดตค่ากำหนดสำหรับตัวควบคุมทั้งหมดหรือตัวควบคุมที่เฉพาะเจาะจง
Kotlin
// Handle "favoritesButton" action, replace by opposite button mediaSession.setMediaButtonPreferences( ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
// Handle "favoritesButton" action, replace by opposite button mediaSession.setMediaButtonPreferences( ImmutableList.of(likeButton, removeFromFavoritesButton));
เพิ่มคําสั่งที่กําหนดเองและปรับแต่งลักษณะการทํางานเริ่มต้น
คำสั่งของโปรแกรมเล่นที่ใช้ได้สามารถขยายได้ด้วยคำสั่งที่กำหนดเอง และคุณยังขัดจังหวะคำสั่งของโปรแกรมเล่นและปุ่มสื่อขาเข้าเพื่อเปลี่ยนลักษณะการทำงานเริ่มต้นได้ด้วย
ประกาศและจัดการคําสั่งที่กําหนดเอง
แอปพลิเคชันสื่อสามารถกำหนดคำสั่งที่กำหนดเองได้ เช่น ใช้ในค่ากำหนดปุ่มสื่อ เช่น คุณอาจต้องการใช้ปุ่มที่อนุญาตให้ผู้ใช้บันทึกรายการสื่อลงในรายการรายการโปรด MediaController
จะส่งคําสั่งที่กําหนดเอง และ MediaSession.Callback
จะรับคําสั่งเหล่านั้น
หากต้องการกำหนดคำสั่งที่กำหนดเอง คุณต้องลบล้าง MediaSession.Callback.onConnect()
เพื่อตั้งค่าคำสั่งที่กำหนดเองที่ใช้ได้สำหรับตัวควบคุมที่เชื่อมต่อแต่ละตัว
Kotlin
private class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
หากต้องการรับคําขอคําสั่งที่กําหนดเองจาก MediaController
ให้ลบล้างวิธี
onCustomCommand()
ใน Callback
Kotlin
private class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
คุณสามารถติดตามว่าตัวควบคุมสื่อใดกำลังส่งคำขอโดยใช้พร็อพเพอร์ตี้ packageName
ของออบเจ็กต์ MediaSession.ControllerInfo
ที่ส่งไปยังเมธอด Callback
ซึ่งช่วยให้คุณปรับแต่งลักษณะการทํางานของแอปเพื่อตอบสนองต่อคําสั่งหนึ่งๆ ได้ หากคําสั่งนั้นมาจากระบบ แอปของคุณเอง หรือแอปไคลเอ็นต์อื่นๆ
ปรับแต่งคำสั่งเริ่มต้นของโปรแกรมเล่น
ระบบจะมอบหมายคําสั่งเริ่มต้นและการจัดการสถานะทั้งหมดให้กับ Player
ใน MediaSession
หากต้องการปรับแต่งลักษณะการทํางานของคําสั่งที่กําหนดไว้ในอินเทอร์เฟซ Player
เช่น play()
หรือ seekToNext()
ให้ใส่ Player
ใน ForwardingSimpleBasePlayer
ก่อนส่งไปยัง MediaSession
ดังนี้
Kotlin
val player = (logic to build a Player instance) val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) { // Customizations } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = (logic to build a Player instance) ForwardingSimpleBasePlayer forwardingPlayer = new ForwardingSimpleBasePlayer(player) { // Customizations }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
ดูข้อมูลเพิ่มเติมเกี่ยวกับ ForwardingSimpleBasePlayer
ได้ที่คู่มือ ExoPlayer เกี่ยวกับการปรับแต่ง
ระบุตัวควบคุมที่ส่งคําขอของคําสั่งผู้เล่น
เมื่อการเรียกใช้เมธอด Player
มาจาก MediaController
คุณสามารถระบุแหล่งที่มาของต้นทางด้วย MediaSession.controllerForCurrentRequest
และรับ ControllerInfo
สําหรับคําขอปัจจุบันได้ ดังนี้
Kotlin
class CallerAwarePlayer(player: Player) : ForwardingSimpleBasePlayer(player) { override fun handleSeek( mediaItemIndex: Int, positionMs: Long, seekCommand: Int, ): ListenableFuture<*> { Log.d( "caller", "seek operation from package ${session.controllerForCurrentRequest?.packageName}", ) return super.handleSeek(mediaItemIndex, positionMs, seekCommand) } }
Java
public class CallerAwarePlayer extends ForwardingSimpleBasePlayer { public CallerAwarePlayer(Player player) { super(player); } @Override protected ListenableFuture<?> handleSeek( int mediaItemIndex, long positionMs, int seekCommand) { Log.d( "caller", "seek operation from package: " + session.getControllerForCurrentRequest().getPackageName()); return super.handleSeek(mediaItemIndex, positionMs, seekCommand); } }
ปรับแต่งการจัดการปุ่มสื่อ
ปุ่มสื่อคือปุ่มฮาร์ดแวร์ที่มีอยู่ในอุปกรณ์ Android และอุปกรณ์ต่อพ่วงอื่นๆ เช่น ปุ่มเล่น/หยุดชั่วคราวบนชุดหูฟังบลูทูธ Media3 จะจัดการเหตุการณ์ปุ่มสื่อให้คุณเมื่อเหตุการณ์มาถึงเซสชันและเรียกใช้Player
เมธอดที่เหมาะสมในโปรแกรมเล่นเซสชัน
เราขอแนะนำให้จัดการเหตุการณ์ปุ่มสื่อขาเข้าทั้งหมดในเมธอด Player
ที่เกี่ยวข้อง สำหรับ Use Case ขั้นสูงขึ้น คุณสามารถสกัดกั้นเหตุการณ์ของปุ่มสื่อใน MediaSession.Callback.onMediaButtonEvent(Intent)
การจัดการและการรายงานข้อผิดพลาด
ข้อผิดพลาดที่เซสชันแสดงและรายงานไปยังตัวควบคุมมี 2 ประเภท ข้อผิดพลาดร้ายแรงจะรายงานการเล่นทางเทคนิคที่ไม่สําเร็จของโปรแกรมเล่นเซสชันที่ขัดจังหวะการเล่น ระบบจะรายงานข้อผิดพลาดร้ายแรงไปยังตัวควบคุมโดยอัตโนมัติเมื่อเกิดข้อผิดพลาด ข้อผิดพลาดที่ไม่ร้ายแรงคือข้อผิดพลาดที่ไม่เกี่ยวข้องกับเทคนิคหรือนโยบาย ซึ่งจะไม่ขัดจังหวะการเล่นและแอปพลิเคชันจะส่งไปยังตัวควบคุมด้วยตนเอง
ข้อผิดพลาดร้ายแรงในการเล่น
โปรแกรมเล่นจะรายงานข้อผิดพลาดในการเล่นร้ายแรงไปยังเซสชัน จากนั้นจะรายงานไปยังตัวควบคุมเพื่อเรียกผ่าน Player.Listener.onPlayerError(PlaybackException)
และ Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException)
ในกรณีเช่นนี้ สถานะการเล่นจะเปลี่ยนเป็น STATE_IDLE
และ MediaController.getPlaybackError()
จะแสดงผล PlaybackException
ที่ทําให้เกิดการเปลี่ยนสถานะ ผู้ควบคุมสามารถตรวจสอบ PlayerException.errorCode
เพื่อดูข้อมูลเกี่ยวกับสาเหตุของข้อผิดพลาด
สําหรับความสามารถในการทํางานร่วมกัน ระบบจะทําซ้ำข้อผิดพลาดร้ายแรงไปยังเซสชันแพลตฟอร์มโดยเปลี่ยนสถานะเป็น STATE_ERROR
และตั้งค่ารหัสและข้อความแสดงข้อผิดพลาดตาม PlaybackException
การปรับแต่งข้อผิดพลาดร้ายแรง
หากต้องการแสดงข้อมูลที่แปลแล้วและมีความหมายต่อผู้ใช้ คุณสามารถปรับแต่งรหัสข้อผิดพลาด ข้อความแสดงข้อผิดพลาด และข้อมูลเพิ่มเติมเกี่ยวกับข้อผิดพลาดในการเล่นร้ายแรงได้โดยใช้ ForwardingPlayer
เมื่อสร้างเซสชัน ดังนี้
Kotlin
val forwardingPlayer = ErrorForwardingPlayer(player) val session = MediaSession.Builder(context, forwardingPlayer).build()
Java
Player forwardingPlayer = new ErrorForwardingPlayer(player); MediaSession session = new MediaSession.Builder(context, forwardingPlayer).build();
โปรแกรมเล่นที่ส่งต่อสามารถใช้ ForwardingSimpleBasePlayer
เพื่อขัดจังหวะข้อผิดพลาดและปรับแต่งรหัสข้อผิดพลาด ข้อความ หรือข้อมูลเพิ่มเติมได้ ในทำนองเดียวกัน คุณยังสร้างข้อผิดพลาดใหม่ที่ไม่มีในโปรแกรมเล่นต้นฉบับได้ด้วย โดยทำดังนี้
Kotlin
class ErrorForwardingPlayer (private val context: Context, player: Player) : ForwardingSimpleBasePlayer(player) { override fun getState(): State { var state = super.getState() if (state.playerError != null) { state = state.buildUpon() .setPlayerError(customizePlaybackException(state.playerError!!)) .build() } return state } fun customizePlaybackException(error: PlaybackException): PlaybackException { val buttonLabel: String val errorMessage: String when (error.errorCode) { PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> { buttonLabel = context.getString(R.string.err_button_label_restart_stream) errorMessage = context.getString(R.string.err_msg_behind_live_window) } else -> { buttonLabel = context.getString(R.string.err_button_label_ok) errorMessage = context.getString(R.string.err_message_default) } } val extras = Bundle() extras.putString("button_label", buttonLabel) return PlaybackException(errorMessage, error.cause, error.errorCode, extras) } }
Java
class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer { private final Context context; public ErrorForwardingPlayer(Context context, Player player) { super(player); this.context = context; } @Override protected State getState() { State state = super.getState(); if (state.playerError != null) { state = state.buildUpon() .setPlayerError(customizePlaybackException(state.playerError)) .build(); } return state; } private PlaybackException customizePlaybackException(PlaybackException error) { String buttonLabel; String errorMessage; switch (error.errorCode) { case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW: buttonLabel = context.getString(R.string.err_button_label_restart_stream); errorMessage = context.getString(R.string.err_msg_behind_live_window); break; default: buttonLabel = context.getString(R.string.err_button_label_ok); errorMessage = context.getString(R.string.err_message_default); break; } Bundle extras = new Bundle(); extras.putString("button_label", buttonLabel); return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras); } }
ข้อผิดพลาดที่ไม่ร้ายแรง
แอปสามารถส่งข้อผิดพลาดที่ไม่ร้ายแรงซึ่งไม่ได้มาจากข้อยกเว้นทางเทคนิคไปยังตัวควบคุมทั้งหมดหรือตัวควบคุมที่เฉพาะเจาะจงได้ ดังนี้
Kotlin
val sessionError = SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired), ) // Option 1: Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError) // Option 2: Sending a nonfatal error to the media notification controller only // to set the error code and error message in the playback state of the platform // media session. mediaSession.mediaNotificationControllerInfo?.let { mediaSession.sendError(it, sessionError) }
Java
SessionError sessionError = new SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired)); // Option 1: Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError); // Option 2: Sending a nonfatal error to the media notification controller only // to set the error code and error message in the playback state of the platform // media session. ControllerInfo mediaNotificationControllerInfo = mediaSession.getMediaNotificationControllerInfo(); if (mediaNotificationControllerInfo != null) { mediaSession.sendError(mediaNotificationControllerInfo, sessionError); }
เมื่อส่งข้อผิดพลาดที่ไม่ร้ายแรงไปยังตัวควบคุมการแจ้งเตือนสื่อ ระบบจะทําซ้ำรหัสข้อผิดพลาดและข้อความแสดงข้อผิดพลาดไปยังเซสชันสื่อของแพลตฟอร์ม ขณะที่PlaybackState.state
จะไม่เปลี่ยนเป็นSTATE_ERROR
รับข้อผิดพลาดที่ไม่ร้ายแรง
MediaController
ได้รับข้อผิดพลาดที่ไม่ร้ายแรงจากการใช้ MediaController.Listener.onError
Kotlin
val future = MediaController.Builder(context, sessionToken) .setListener(object : MediaController.Listener { override fun onError(controller: MediaController, sessionError: SessionError) { // Handle nonfatal error. } }) .buildAsync()
Java
MediaController.Builder future = new MediaController.Builder(context, sessionToken) .setListener( new MediaController.Listener() { @Override public void onError(MediaController controller, SessionError sessionError) { // Handle nonfatal error. } });