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