Jetpack Media3 กำหนดอินเทอร์เฟซ Player ที่ระบุฟังก์ชันพื้นฐาน
สำหรับการเล่นไฟล์วิดีโอและเสียง ExoPlayer เป็นการใช้งานเริ่มต้น
ของอินเทอร์เฟซนี้ใน Media3 เราขอแนะนำให้ใช้ ExoPlayer เนื่องจากมีชุดฟีเจอร์ที่ครอบคลุม Use Case การเล่นส่วนใหญ่และปรับแต่งได้เพื่อรองรับ Use Case เพิ่มเติมที่คุณอาจมี นอกจากนี้ ExoPlayer ยัง
แยกความแตกต่างของอุปกรณ์และระบบปฏิบัติการออกเพื่อให้โค้ดทำงานได้อย่างสอดคล้องกัน
ทั่วทั้งระบบนิเวศของ Android ExoPlayer มีฟีเจอร์ต่อไปนี้
- การรองรับเพลย์ลิสต์
- การรองรับรูปแบบการสตรีมแบบโปรเกรสซีฟและแบบปรับได้ที่หลากหลาย รูปแบบ
- การรองรับการแทรกโฆษณาฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์ ad insertion
- การรองรับการเล่นที่ป้องกันด้วย DRM
หน้านี้จะแนะนำขั้นตอนสำคัญบางอย่างในการสร้างแอปการเล่น และหากต้องการดูรายละเอียดเพิ่มเติม โปรดไปที่คำแนะนำฉบับเต็มเกี่ยวกับ Media3 ExoPlayer
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งาน ให้เพิ่มการอ้างอิงในโมดูล ExoPlayer, UI และ Common ของ Jetpack Media3 ดังนี้
implementation "androidx.media3:media3-exoplayer:1.10.0" implementation "androidx.media3:media3-ui:1.10.0" implementation "androidx.media3:media3-common:1.10.0"
คุณอาจต้องใช้โมดูลเพิ่มเติมจาก Media3 ด้วย โดยขึ้นอยู่กับ Use Case
เช่น exoplayer-dash เพื่อเล่นสตรีมในรูปแบบ DASH
โปรดตรวจสอบว่าได้แทนที่ 1.10.0 ด้วยไลบรารีเวอร์ชันที่ต้องการแล้ว คุณสามารถดูเวอร์ชันล่าสุดได้ใน บันทึกประจำรุ่น
การสร้างมีเดียเพลเยอร์
Media3 ช่วยให้คุณใช้การติดตั้งใช้งาน ExoPlayer ซึ่งเป็นอินเทอร์เฟซ Player
ที่รวมไว้ หรือสร้างการติดตั้งใช้งานที่กำหนดเองได้
การสร้าง ExoPlayer
วิธีที่ง่ายที่สุดในการสร้างอินสแตนซ์ ExoPlayer มีดังนี้
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
คุณสามารถสร้างมีเดียเพลเยอร์ในเมธอดวงจร onCreate() ของ Activity, Fragment หรือ Service ที่มีมีเดียเพลเยอร์อยู่
Builder มีตัวเลือกการปรับแต่งที่หลากหลายที่คุณอาจสนใจ เช่น
setAudioAttributes()เพื่อกำหนดค่าการจัดการโฟกัสเสียงsetHandleAudioBecomingNoisy()เพื่อกำหนดค่าลักษณะการทำงานของการเล่นเมื่ออุปกรณ์เอาต์พุตเสียงถูกตัดการเชื่อมต่อsetTrackSelector()เพื่อกำหนดค่าการเลือกแทร็ก
Media3 มีคอมโพเนนต์ UI PlayerView ที่คุณสามารถรวมไว้ในไฟล์เลย์เอาต์ของแอปได้ คอมโพเนนต์นี้ห่อหุ้ม PlayerControlView สำหรับตัวควบคุมการเล่น SubtitleView สำหรับแสดงคำบรรยาย และ Surface สำหรับแสดงวิดีโอ
กำลังเตรียมเพลเยอร์
เพิ่มรายการสื่อลงในเพลย์ลิสต์เพื่อ
เล่นด้วยเมธอดต่างๆ เช่น
setMediaItem()
และ addMediaItem()
จากนั้นเรียกใช้ prepare() เพื่อ
เริ่มโหลดสื่อและรับทรัพยากรที่จำเป็น
คุณไม่ควรทำตามขั้นตอนเหล่านี้ก่อนที่แอปจะทำงานในเบื้องหน้า หากเพลเยอร์อยู่ใน Activity หรือ Fragment แสดงว่าต้องเตรียมเพลเยอร์ในเมธอดวงจร onStart() ใน API ระดับ 24 ขึ้นไป หรือเมธอดวงจร onResume() ใน API ระดับ 23 ลงไป สำหรับเพลเยอร์ที่อยู่ใน Service
คุณสามารถเตรียมผู้เล่นใน onCreate() ดูตัวอย่างวิธีใช้เมธอดวงจรได้ที่ Exoplayer codelab
ควบคุมเพลเยอร์
หลังจากเตรียมเพลเยอร์แล้ว คุณสามารถควบคุมการเล่นได้โดยเรียกใช้เมธอดในเพลเยอร์ เช่น
play()และpause()เพื่อเริ่มและหยุดการเล่นชั่วคราวseekTo()เพื่อค้นหาตำแหน่งภายในรายการสื่อปัจจุบันseekToNextMediaItem()และseekToPreviousMediaItem()เพื่อเลื่อนดูเพลย์ลิสต์
คอมโพเนนต์ UI เช่น PlayerView หรือ PlayerControlView จะอัปเดตตามความเหมาะสมเมื่อผูกกับเพลเยอร์
ปล่อยเพลเยอร์
การเล่นอาจต้องใช้ทรัพยากรที่มีจำกัด เช่น ตัวถอดรหัสวิดีโอ ดังนั้นคุณจึงควรเรียกใช้ release()
ในเพลเยอร์เพื่อปล่อยทรัพยากรเมื่อไม่จำเป็นต้องใช้เพลเยอร์อีกต่อไป
หากเพลเยอร์อยู่ใน Activity หรือ Fragment ให้ปล่อยเพลเยอร์ในเมธอดวงจร onStop() ใน API ระดับ 24 ขึ้นไป หรือเมธอด onPause() ใน API ระดับ 23 ลงไป สำหรับเพลเยอร์ที่อยู่ใน Service คุณสามารถ
ปล่อยใน onDestroy() ได้ ดูตัวอย่างวิธีใช้เมธอดวงจรได้ที่ Exoplayer Codelab
การจัดการการเล่นด้วยเซสชันสื่อ
ใน Android เซสชันสื่อจะมอบวิธีมาตรฐานในการโต้ตอบกับมีเดียเพลเยอร์ข้ามกระบวนการ การเชื่อมต่อเซสชันสื่อกับเพลเยอร์ ช่วยให้คุณโฆษณาการเล่นสื่อภายนอกและรับคำสั่งการเล่น จากแหล่งที่มาภายนอกได้ เช่น เพื่อผสานรวมกับตัวควบคุมสื่อของระบบในมือถือและอุปกรณ์ที่มีหน้าจอขนาดใหญ่
หากต้องการใช้เซสชันสื่อ ให้เพิ่มการอ้างอิงในโมดูล Media3 Session ดังนี้
implementation "androidx.media3:media3-session:1.10.0"
สร้างเซสชันสื่อ
คุณสามารถสร้าง MediaSession หลังจากเริ่มต้นเพลเยอร์ได้ดังนี้
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 จะซิงค์สถานะของ Player กับสถานะของ MediaSession โดยอัตโนมัติ ซึ่งจะทำงานกับการติดตั้งใช้งาน Player ทุกรายการ รวมถึง
ExoPlayer, CastPlayer หรือการติดตั้งใช้งานที่
กำหนดเอง
ให้สิทธิ์ควบคุมแก่ไคลเอ็นต์อื่นๆ
แอปไคลเอ็นต์สามารถใช้ตัวควบคุมสื่อ
เพื่อควบคุมการเล่นเซสชันสื่อได้ หากต้องการรับคำขอเหล่านี้ ให้ตั้งค่าออบเจ็กต์ Callback เมื่อสร้าง MediaSession
เมื่อตัวควบคุมกำลังจะเชื่อมต่อกับเซสชันสื่อ ระบบจะเรียกใช้เมธอด
onConnect()
คุณสามารถใช้ ControllerInfo
เพื่อตัดสินใจว่าจะยอมรับ
หรือปฏิเสธ
คำขอ ดูตัวอย่างได้ในแอปเดโมของ Media3 Session
เมื่อเชื่อมต่อแล้ว ตัวควบคุมจะส่งคำสั่งการเล่นไปยังเซสชันได้ จากนั้นเซสชันจะมอบหมายคำสั่งเหล่านั้นไปยังเพลเยอร์ ระบบจะจัดการคำสั่งการเล่นและเพลย์ลิสต์ที่กำหนดไว้ในอินเทอร์เฟซ Player โดยอัตโนมัติ
เมธอด Callback อื่นๆ ช่วยให้คุณจัดการคำขอต่างๆ ได้ เช่น คำสั่งเล่นที่กำหนดเอง และการแก้ไขเพลย์ลิสต์ โดยการเรียกกลับเหล่านี้จะมีออบเจ็กต์ ControllerInfo เช่นเดียวกัน เพื่อให้คุณ
กำหนดการควบคุมการเข้าถึงตามคำขอแต่ละรายการได้
การเล่นสื่อขณะล็อกหน้าจอหรือขณะใช้แอปอื่น
หากต้องการเล่นสื่อต่อไปเมื่อแอปไม่ได้ทำงานอยู่เบื้องหน้า เช่น
เล่นเพลง หนังสือเสียง หรือพอดแคสต์ แม้ว่าผู้ใช้จะไม่ได้เปิดแอปอยู่
ควรห่อหุ้มข้อมูล Player และ MediaSession ไว้ในบริการที่ทำงานอยู่เบื้องหน้า Media3 มีอินเทอร์เฟซ MediaSessionService เพื่อวัตถุประสงค์นี้
การติดตั้งใช้งาน MediaSessionService
สร้างคลาสที่ขยาย MediaSessionService และสร้างอินสแตนซ์
MediaSession ในเมธอดวงจร onCreate()
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your Player and MediaSession in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
ในไฟล์ Manifest ให้เพิ่มคลาส Service ที่มีตัวกรอง Intent ของ MediaSessionService และขอสิทธิ์ FOREGROUND_SERVICE เพื่อเรียกใช้บริการที่ทำงานอยู่เบื้องหน้า
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
สุดท้าย ในคลาสที่คุณสร้าง ให้ลบล้างเมธอด onGetSession() เพื่อควบคุมการเข้าถึงเซสชันสื่อของไคลเอ็นต์ ส่งคืน MediaSession เพื่อยอมรับคำขอการเชื่อมต่อ หรือส่งคืน null เพื่อปฏิเสธคำขอ
Kotlin
// This example always accepts the connection request override fun onGetSession( controllerInfo: MediaSession.ControllerInfo ): MediaSession? = mediaSession
Java
@Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // This example always accepts the connection request return mediaSession; }
การเชื่อมต่อกับ UI
ตอนนี้เซสชันสื่อของคุณอยู่ใน Service แยกจาก Activity หรือ Fragment ที่ UI ของเพลเยอร์อยู่ คุณสามารถใช้ MediaController เพื่อลิงก์รายการเหล่านี้เข้าด้วยกันได้ ในเมธอด onStart() ของ Activity หรือ Fragment ที่มี UI ของคุณ ให้สร้าง SessionToken สำหรับ MediaSession จากนั้นใช้ SessionToken เพื่อสร้าง MediaController การสร้าง MediaController จะเกิดขึ้น
แบบอะซิงโครนัส
Kotlin
override fun onStart() { val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()) }, MoreExecutors.directExecutor() ) }
Java
@Override public void onStart() { SessionToken sessionToken = new SessionToken(this, new ComponentName(this, PlaybackService.class)); ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(this, sessionToken).buildAsync(); controllerFuture.addListener(() -> { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()); }, MoreExecutors.directExecutor()) }
MediaController ใช้อินเทอร์เฟซ Player คุณจึงสามารถใช้วิธีการเดียวกัน เช่น play() และ pause() เพื่อควบคุมการเล่น เช่นเดียวกับคอมโพเนนต์อื่นๆ อย่าลืมปล่อย MediaController เมื่อไม่จำเป็นต้องใช้แล้ว
เช่น เมธอดวงจร onStop() ของ Activity โดยการเรียก MediaController.releaseFuture()
การเผยแพร่การแจ้งเตือน
บริการที่ทำงานอยู่เบื้องหน้าต้องเผยแพร่การแจ้งเตือนขณะที่ทำงานอยู่ MediaSessionService จะสร้างการแจ้งเตือน MediaStyle ให้คุณโดยอัตโนมัติในรูปแบบของ MediaNotification
หากต้องการแสดงการแจ้งเตือนที่กำหนดเอง ให้สร้าง
MediaNotification.Provider
ด้วย DefaultMediaNotificationProvider.Builder
หรือสร้างการติดตั้งใช้งานอินเทอร์เฟซผู้ให้บริการที่กำหนดเอง เพิ่มผู้ให้บริการลงใน MediaSession ด้วย setMediaNotificationProvider
การโฆษณาคลังเนื้อหา
MediaLibraryService สร้างขึ้นจาก MediaSessionService โดยอนุญาตให้แอปไคลเอ็นต์เรียกดูเนื้อหาสื่อที่แอปของคุณมีให้ แอปไคลเอ็นต์จะติดตั้งใช้งานMediaBrowser เพื่อโต้ตอบกับ MediaLibraryService ของคุณ
การติดตั้งใช้งาน MediaLibraryService คล้ายกับการติดตั้งใช้งาน
MediaSessionService ยกเว้นว่าใน onGetSession() คุณควรส่งคืน
MediaLibrarySession แทน MediaSession เมื่อเทียบกับ MediaSession.Callback แล้ว MediaLibrarySession.Callback มีเมธอดเพิ่มเติม
ที่ช่วยให้ไคลเอ็นต์เบราว์เซอร์ไปยังส่วนต่างๆ ของเนื้อหาที่บริการห้องสมุดของคุณนำเสนอได้
เช่นเดียวกับ MediaSessionService ให้ประกาศ MediaLibraryService ในไฟล์ Manifest และขอสิทธิ์ FOREGROUND_SERVICE เพื่อเรียกใช้บริการที่ทำงานอยู่เบื้องหน้า
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
ตัวอย่างด้านบนมีตัวกรอง Intent สำหรับทั้ง MediaLibraryService และ MediaBrowserService เดิมเพื่อให้เข้ากันได้แบบย้อนหลัง ตัวกรอง Intent เพิ่มเติมช่วยให้แอปไคลเอ็นต์ที่ใช้ API MediaBrowserCompat จดจำ Service ของคุณได้
MediaLibrarySession ช่วยให้คุณแสดงคลังเนื้อหาในโครงสร้างแบบต้นไม้ โดยมี MediaItem เพียงรูทเดียว MediaItem แต่ละรายการในโครงสร้างจะมีโหนด MediaItem ย่อยกี่รายการก็ได้
คุณสามารถแสดงรูทอื่นหรือโครงสร้างอื่นตามคำขอของแอปไคลเอ็นต์ ตัวอย่างเช่น โครงสร้างที่คุณส่งคืนให้ไคลเอ็นต์ที่กำลังมองหารายการสื่อที่แนะนำอาจมีเพียงรูท MediaItem และโหนดลูก MediaItem ระดับเดียว ในขณะที่โครงสร้างที่คุณส่งคืนให้แอปไคลเอ็นต์อื่นอาจแสดงคลังเนื้อหาที่สมบูรณ์กว่า
การสร้าง MediaLibrarySession
MediaLibrarySession
ขยาย MediaSession API เพื่อเพิ่ม API การเรียกดูเนื้อหา เมื่อเทียบกับ MediaSession Callback แล้ว MediaLibrarySession Callback จะเพิ่มเมธอดต่างๆ เช่น
onGetLibraryRoot()เมื่อไคลเอ็นต์ขอรูทMediaItemของโครงสร้างเนื้อหาonGetChildren()เมื่อไคลเอ็นต์ขอรายการย่อยของMediaItemในโครงสร้างเนื้อหาonGetSearchResult()เมื่อไคลเอ็นต์ขอผลการค้นหาจากโครงสร้างเนื้อหาสำหรับคำค้นหาที่กำหนด
เมธอดเรียกกลับที่เกี่ยวข้องจะมีออบเจ็กต์ LibraryParams พร้อมสัญญาณเพิ่มเติมเกี่ยวกับประเภทของโครงสร้างเนื้อหาที่แอปไคลเอ็นต์สนใจ