Jetpack Media3 กำหนดPlayer
อินเทอร์เฟซที่ระบุฟังก์ชันพื้นฐานสำหรับการเล่นไฟล์วิดีโอและไฟล์เสียง ExoPlayer
เป็นการใช้งานเริ่มต้นของอินเทอร์เฟซนี้ใน Media3 เราขอแนะนำให้ใช้ ExoPlayer เนื่องจากมีชุดฟีเจอร์ที่ครอบคลุมซึ่งครอบคลุม Use Case การเล่นส่วนใหญ่ และปรับแต่งได้เพื่อจัดการกับ Use Case เพิ่มเติมที่คุณอาจมี นอกจากนี้ ExoPlayer ยังช่วยแยกความแตกต่างของอุปกรณ์และระบบปฏิบัติการเพื่อให้โค้ดของคุณทำงานได้อย่างสอดคล้องกันทั่วทั้งระบบนิเวศ Android ExoPlayer ประกอบด้วย
- การรองรับเพลย์ลิสต์
- รองรับรูปแบบสตรีมมิงแบบ Progressive และแบบปรับอัตโนมัติที่หลากหลาย
- รองรับการแทรกโฆษณาทั้งฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์
- การรองรับการเล่นที่ได้รับการคุ้มครอง DRM
หน้านี้จะแนะนำขั้นตอนสำคัญในการสร้างแอปการเล่น และดูรายละเอียดเพิ่มเติมได้ที่คู่มือฉบับเต็มเกี่ยวกับ Media3 ExoPlayer
เริ่มต้นใช้งาน
ในการเริ่มต้นใช้งาน ให้เพิ่มการพึ่งพาในโมดูล ExoPlayer, UI และ Common ของ Jetpack Media3
implementation "androidx.media3:media3-exoplayer:1.5.0" implementation "androidx.media3:media3-ui:1.5.0" implementation "androidx.media3:media3-common:1.5.0"
คุณอาจต้องใช้โมดูลเพิ่มเติมจาก Media3 เช่น exoplayer-dash
เพื่อเล่นสตรีมในรูปแบบ DASH ด้วย ทั้งนี้ขึ้นอยู่กับกรณีการใช้งาน
โปรดตรวจสอบว่าได้แทนที่ 1.5.0
ด้วยไลบรารีเวอร์ชันที่ต้องการ คุณสามารถดูบันทึกประจำรุ่นเพื่อดูเวอร์ชันล่าสุด
การสร้างโปรแกรมเล่นสื่อ
เมื่อใช้ Media3 คุณจะใช้การติดตั้งใช้งานPlayer
อินเทอร์เฟซ ExoPlayer
ที่รวมอยู่ด้วย หรือจะสร้างการติดตั้งใช้งานที่กําหนดเองก็ได้
การสร้าง 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()
lifecycle method ใน API ระดับ 24 ขึ้นไป หรือ onResume()
lifecycle method ใน API ระดับ 23 และต่ำกว่า สำหรับผู้เล่นที่อยู่ใน Service
คุณสามารถเตรียมความพร้อมใน onCreate()
ควบคุมเพลเยอร์
หลังจากเตรียมเพลเยอร์แล้ว คุณจะควบคุมการเล่นได้โดยเรียกใช้เมธอดในเพลเยอร์ เช่น
play()
และpause()
เพื่อเริ่มและหยุดเล่นชั่วคราวseekTo()
เพื่อกรอไปยังตำแหน่งภายในรายการสื่อปัจจุบันseekToNextMediaItem()
และseekToPreviousMediaItem()
เพื่อไปยังส่วนต่างๆ ของเพลย์ลิสต์
คอมโพเนนต์ UI เช่น PlayerView
หรือ PlayerControlView
จะอัปเดตตามนั้นเมื่อเชื่อมโยงกับวิดีโอเพลเยอร์
ปล่อยผู้เล่น
การเล่นอาจต้องใช้ทรัพยากรที่มีอย่างจำกัด เช่น ตัวถอดรหัสวิดีโอ คุณจึงควรเรียกใช้ release()
ในโปรแกรมเล่นเพื่อเพิ่มพื้นที่ว่างสำหรับทรัพยากรเมื่อไม่จำเป็นต้องใช้โปรแกรมเล่นอีกต่อไป
หากเพลเยอร์อยู่ใน Activity
หรือ Fragment
ให้ปล่อยเพลเยอร์ในวิธีวงจรชีวิตของ onStop()
ใน API ระดับ 24 ขึ้นไป หรือวิธี onPause()
ใน API ระดับ 23 และต่ำกว่า สำหรับผู้เล่นที่อยู่ใน Service
คุณจะเผยแพร่ได้ใน onDestroy()
การจัดการการเล่นด้วยเซสชันสื่อ
ใน Android เซสชันสื่อเป็นวิธีที่เป็นมาตรฐานในการโต้ตอบกับโปรแกรมเล่นสื่อในขอบเขตของกระบวนการต่างๆ การเชื่อมต่อเซสชันสื่อกับโปรแกรมเล่นช่วยให้คุณโฆษณาการเล่นสื่อภายนอกและรับคำสั่งการเล่นจากแหล่งที่มาภายนอกได้ เช่น เพื่อผสานรวมกับการควบคุมสื่อของระบบในอุปกรณ์เคลื่อนที่และอุปกรณ์หน้าจอขนาดใหญ่
หากต้องการใช้เซสชันสื่อ ให้เพิ่มการพึ่งพาในโมดูลเซสชัน Media3 โดยทำดังนี้
implementation "androidx.media3:media3-session:1.5.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
หรือการติดตั้งใช้งานที่กําหนดเอง
มอบสิทธิ์ควบคุมให้กับไคลเอ็นต์รายอื่น
แอปไคลเอ็นต์สามารถใช้ตัวควบคุมสื่อเพื่อควบคุมการเล่นเซสชันสื่อ หากต้องการรับคําขอเหล่านี้ ให้ตั้งค่าออบเจ็กต์การเรียกคืนเมื่อสร้าง MediaSession
เมื่อตัวควบคุมกำลังจะเชื่อมต่อกับเซสชันสื่อ ระบบจะเรียกใช้เมธอด onConnect()
คุณสามารถใช้ ControllerInfo
ที่ระบุไว้เพื่อตัดสินใจว่าจะยอมรับหรือปฏิเสธคําขอ ดูตัวอย่างได้ในแอปเดโมเซสชัน Media3
เมื่อเชื่อมต่อแล้ว ตัวควบคุมจะส่งคำสั่งการเล่นไปยังเซสชันได้ จากนั้นเซสชันจะมอบสิทธิ์คำสั่งเหล่านั้นไปยังโปรแกรมเล่น เซสชันจะจัดการคำสั่งการเล่นและเพลย์ลิสต์ที่กําหนดไว้ในอินเทอร์เฟซ Player
โดยอัตโนมัติ
วิธีการเรียกกลับอื่นๆ ช่วยให้คุณจัดการกับคำขอต่างๆ ได้ เช่น คำขอคำสั่งการเล่นที่กำหนดเองและการแก้ไขเพลย์ลิสต์ แคล็กแบ็กเหล่านี้จะมีออบเจ็กต์ 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
จะมีวิธีการเพิ่มเติมที่ช่วยให้ไคลเอ็นต์เบราว์เซอร์ไปยังส่วนต่างๆ ของเนื้อหาที่บริการคลังของคุณนำเสนอได้
ประกาศ MediaLibraryService
ในไฟล์ Manifest และขอสิทธิ์ FOREGROUND_SERVICE
เพื่อเรียกใช้บริการที่ทำงานอยู่เบื้องหน้า เช่นเดียวกับ MediaSessionService
<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
การเรียกกลับ MediaLibrarySession
เพิ่มเมธอดต่างๆ เช่น
onGetLibraryRoot()
สำหรับกรณีที่ไคลเอ็นต์ขอรูทMediaItem
ของต้นไม้เนื้อหาonGetChildren()
สำหรับกรณีที่ไคลเอ็นต์ขอรายการย่อยของMediaItem
ในโครงสร้างเนื้อหาonGetSearchResult()
สำหรับกรณีที่ไคลเอ็นต์ขอผลการค้นหาจากต้นไม้เนื้อหาสำหรับข้อความค้นหาหนึ่งๆ
เมธอดการเรียกกลับที่เกี่ยวข้องจะมีออบเจ็กต์ LibraryParams
ที่มีสัญญาณเพิ่มเติมเกี่ยวกับประเภทของโครงสร้างเนื้อหาที่แอปไคลเอ็นต์สนใจ