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