Jetpack Media3 กำหนดอินเทอร์เฟซ Player ที่ระบุฟังก์ชันพื้นฐาน
สำหรับการเล่นไฟล์วิดีโอและเสียง ExoPlayer เป็นการใช้งานเริ่มต้น
ของอินเทอร์เฟซนี้ใน Media3 เราขอแนะนำให้ใช้ ExoPlayer เนื่องจากมีชุดฟีเจอร์ที่ครอบคลุม Use Case การเล่นส่วนใหญ่และปรับแต่งได้เพื่อรองรับ Use Case เพิ่มเติมที่คุณอาจมี นอกจากนี้ ExoPlayer ยัง
แยกความแตกต่างของอุปกรณ์และระบบปฏิบัติการออกเพื่อให้โค้ดทำงานได้อย่างสอดคล้องกัน
ทั่วทั้งระบบนิเวศของ Android ExoPlayer มีฟีเจอร์ต่อไปนี้
- การรองรับเพลย์ลิสต์
- รองรับการสตรีมแบบก้าวหน้าและแบบปรับอัตโนมัติหลากหลายรูปแบบ
- รองรับทั้งการแทรกโฆษณาฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์
- รองรับการเล่นที่ได้รับการป้องกันด้วย DRM
หน้านี้จะแนะนำขั้นตอนสำคัญบางอย่างในการสร้างแอปการเล่น และดูรายละเอียดเพิ่มเติมได้ที่คำแนะนำฉบับเต็มเกี่ยวกับ Media3 ExoPlayer
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งาน ให้เพิ่มการอ้างอิงในโมดูล ExoPlayer, UI และ Common ของ Jetpack Media3 ดังนี้
implementation "androidx.media3:media3-exoplayer:1.8.0" implementation "androidx.media3:media3-ui:1.8.0" implementation "androidx.media3:media3-common:1.8.0"
คุณอาจต้องใช้โมดูลเพิ่มเติมจาก Media3 ด้วย โดยขึ้นอยู่กับ Use Case
เช่น exoplayer-dash เพื่อเล่นสตรีมในรูปแบบ DASH
โปรดตรวจสอบว่าได้แทนที่ 1.8.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 มีPlayerViewคอมโพเนนต์ UI ที่คุณสามารถรวมไว้ใน
ไฟล์เลย์เอาต์ของแอป คอมโพเนนต์นี้ห่อหุ้ม 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.8.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 พร้อมตัวกรอง MediaSessionService Intent
และขอสิทธิ์ 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 เพิ่มเติมช่วยให้แอปไคลเอ็นต์ที่ใช้ MediaBrowserCompat API
รู้จัก Service
MediaLibrarySession ช่วยให้คุณแสดงคลังเนื้อหาในโครงสร้างแบบต้นไม้ โดยมี MediaItem เพียงรูทเดียว MediaItem แต่ละรายการในโครงสร้างจะมีโหนด MediaItem ย่อยกี่รายการก็ได้
คุณสามารถแสดงรูทอื่นหรือโครงสร้างอื่นตามคำขอของแอปไคลเอ็นต์ ตัวอย่างเช่น โครงสร้างที่คุณส่งคืนให้ไคลเอ็นต์ที่กำลังมองหารายการสื่อที่แนะนำอาจมีเพียงรูท MediaItem และโหนดลูก MediaItem ระดับเดียว ในขณะที่โครงสร้างที่คุณส่งคืนให้แอปไคลเอ็นต์อื่นอาจแสดงคลังเนื้อหาที่สมบูรณ์กว่า
การสร้าง MediaLibrarySession
MediaLibrarySession
ขยาย MediaSession API เพื่อเพิ่ม API การเรียกดูเนื้อหา เมื่อเทียบกับ MediaSession Callback แล้ว MediaLibrarySession Callback จะเพิ่มเมธอดต่างๆ เช่น
onGetLibraryRoot()เมื่อไคลเอ็นต์ขอรูทMediaItemของโครงสร้างเนื้อหาonGetChildren()เมื่อไคลเอ็นต์ขอองค์ประกอบย่อยของMediaItemในโครงสร้างเนื้อหาonGetSearchResult()เมื่อไคลเอ็นต์ขอผลการค้นหาจากโครงสร้างเนื้อหาสำหรับคำค้นหาที่กำหนด
เมธอดเรียกกลับที่เกี่ยวข้องจะมีออบเจ็กต์ LibraryParams พร้อมสัญญาณเพิ่มเติมเกี่ยวกับประเภทของโครงสร้างเนื้อหาที่แอปไคลเอ็นต์สนใจ