เซสชันสื่อเป็นวิธีสากลในการโต้ตอบกับโปรแกรมเล่นเสียงหรือวิดีโอ ใน Media3 โปรแกรมเล่นเริ่มต้นคือคลาส ExoPlayer
ซึ่งใช้อินเทอร์เฟซ Player
การเชื่อมต่อเซสชันสื่อกับโปรแกรมเล่นช่วยให้แอปโฆษณาการเล่นสื่อภายนอกและรับคำสั่งการเล่นจากแหล่งที่มาภายนอกได้
คำสั่งอาจมาจากปุ่มจริง เช่น ปุ่มเล่นบนหูฟังหรือรีโมตทีวี นอกจากนี้ คำสั่งอาจมาจากแอปไคลเอ็นต์ที่มีตัวควบคุมสื่อ เช่น คำสั่ง "หยุดชั่วคราว" ไปยัง Google Assistant เซสชันสื่อจะมอบสิทธิ์คำสั่งเหล่านี้ให้กับโปรแกรมเล่นของแอปสื่อ
กรณีที่ควรเลือกเซสชันสื่อ
เมื่อใช้ MediaSession
คุณจะอนุญาตให้ผู้ใช้ควบคุมการเล่นได้ดังนี้
- ผ่านหูฟัง มักจะมีปุ่มหรือการโต้ตอบด้วยการสัมผัสที่ผู้ใช้สามารถดำเนินการกับหูฟังเพื่อเล่นหรือหยุดสื่อชั่วคราว หรือไปยังแทร็กถัดไปหรือก่อนหน้า
- โดยพูดกับ Google Assistant รูปแบบคำสั่งทั่วไปคือพูดว่า "Ok Google หยุดชั่วคราว" เพื่อหยุดสื่อที่เล่นอยู่ในอุปกรณ์ชั่วคราว
- ผ่านนาฬิกา Wear OS วิธีนี้ช่วยให้ผู้ใช้เข้าถึงการควบคุมการเล่นที่ใช้บ่อยที่สุดได้ง่ายขึ้นขณะเล่นบนโทรศัพท์
- ผ่านตัวควบคุมสื่อ ภาพสไลด์นี้จะแสดงตัวควบคุมสำหรับเซสชันสื่อที่เล่นอยู่แต่ละรายการ
- บนทีวี อนุญาตให้ดำเนินการกับปุ่มเล่นจริง การควบคุมการเล่นบนแพลตฟอร์ม และการจัดการพลังงาน (เช่น หากทีวี ซาวด์บาร์ หรือรีซีฟเวอร์ A/V ปิดอยู่หรือมีการสลับอินพุต การเล่นในแอปควรหยุดลง)
- และกระบวนการภายนอกอื่นๆ ที่ต้องส่งผลต่อการเล่น
ซึ่งเหมาะสําหรับ Use Case หลายรายการ โดยเฉพาะอย่างยิ่ง คุณควรพิจารณาใช้ MediaSession
ในกรณีต่อไปนี้
- คุณกำลังสตรีมเนื้อหาวิดีโอแบบยาว เช่น ภาพยนตร์หรือรายการทีวีสด
- คุณกำลังสตรีมเนื้อหาเสียงแบบยาว เช่น พอดแคสต์หรือเพลย์ลิสต์เพลง
- คุณกำลังสร้างแอปทีวี
อย่างไรก็ตาม กรณีการใช้งานบางรายการอาจไม่เหมาะกับ MediaSession
คุณอาจต้อง
ใช้เฉพาะ 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. } });