สร้างแอปมีเดียเพลเยอร์พื้นฐานโดยใช้ Media3 ExoPlayer

Jetpack Media3 กำหนดอินเทอร์เฟซ Player ที่ระบุฟังก์ชันพื้นฐาน สำหรับการเล่นไฟล์วิดีโอและเสียง ExoPlayer เป็นการใช้งานเริ่มต้น ของอินเทอร์เฟซนี้ใน Media3 เราขอแนะนำให้ใช้ ExoPlayer เนื่องจากมีชุดฟีเจอร์ที่ครอบคลุมกรณีการใช้งานการเล่นส่วนใหญ่และปรับแต่งได้เพื่อรองรับกรณีการใช้งานเพิ่มเติมที่คุณอาจมี นอกจากนี้ ExoPlayer ยัง แยกความแตกต่างของอุปกรณ์และระบบปฏิบัติการออกเพื่อให้โค้ดทำงานได้อย่างสม่ำเสมอ ในระบบนิเวศ Android ทั้งหมด ExoPlayer มีฟีเจอร์ต่อไปนี้

หน้านี้จะแนะนำขั้นตอนสำคัญบางอย่างในการสร้างแอปการเล่น และดูรายละเอียดเพิ่มเติมได้ที่คำแนะนำฉบับเต็มเกี่ยวกับ Media3 ExoPlayer

เริ่มต้นใช้งาน

หากต้องการเริ่มต้นใช้งาน ให้เพิ่มการอ้างอิงในโมดูล ExoPlayer, UI และ Common ของ Jetpack Media3 ดังนี้

implementation "androidx.media3:media3-exoplayer:1.7.1"
implementation "androidx.media3:media3-ui:1.7.1"
implementation "androidx.media3:media3-common:1.7.1"

คุณอาจต้องใช้โมดูลเพิ่มเติมจาก Media3 ด้วย โดยขึ้นอยู่กับกรณีการใช้งาน เช่น exoplayer-dash เพื่อเล่นสตรีมในรูปแบบ DASH

โปรดตรวจสอบว่าได้แทนที่ 1.7.1 ด้วยไลบรารีเวอร์ชันที่ต้องการแล้ว คุณดูเวอร์ชันล่าสุดได้ในบันทึกประจำรุ่น

การสร้างมีเดียเพลเยอร์

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 มีตัวเลือกการปรับแต่งมากมายที่คุณอาจสนใจ เช่น

Media3 มีPlayerViewคอมโพเนนต์ UI ที่คุณสามารถรวมไว้ใน ไฟล์เลย์เอาต์ของแอป คอมโพเนนต์นี้ห่อหุ้ม PlayerControlView สำหรับตัวควบคุมการเล่น SubtitleView สำหรับการแสดงคำบรรยาย และ Surface สำหรับการแสดงผล วิดีโอ

กำลังเตรียมโปรแกรมเล่น

เพิ่มรายการสื่อลงในเพลย์ลิสต์เพื่อ เล่นด้วยวิธีต่างๆ เช่น setMediaItem() และ addMediaItem() จากนั้นเรียกใช้ prepare() เพื่อ เริ่มโหลดสื่อและรับทรัพยากรที่จำเป็น

คุณไม่ควรทำตามขั้นตอนเหล่านี้ก่อนที่แอปจะทำงานในเบื้องหน้า หากเพลเยอร์อยู่ใน Activity หรือ Fragment แสดงว่าต้องเตรียมเพลเยอร์ในเมธอดวงจร onStart() ใน API ระดับ 24 ขึ้นไป หรือเมธอดวงจร onResume() ใน 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.7.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 หรือ การติดตั้งใช้งานที่กำหนดเอง

มอบสิทธิ์ควบคุมให้กับไคลเอ็นต์อื่นๆ

แอปไคลเอ็นต์สามารถใช้ตัวควบคุมสื่อ เพื่อควบคุมการเล่นเซสชันสื่อได้ หากต้องการรับคำขอเหล่านี้ ให้ตั้งค่าออบเจ็กต์ การเรียกกลับเมื่อสร้าง MediaSession

เมื่อกำลังจะเชื่อมต่อคอนโทรลเลอร์กับเซสชันสื่อ ระบบจะเรียกใช้เมธอด onConnect() คุณสามารถใช้ControllerInfo ที่ระบุไว้ เพื่อตัดสินใจว่าจะยอมรับ หรือปฏิเสธ คำขอ ดูตัวอย่างได้ในแอปตัวอย่างของ Media3 Session

เมื่อเชื่อมต่อแล้ว คอนโทรลเลอร์จะส่งคำสั่งการเล่นไปยังเซสชันได้ จากนั้น เซสชันจะมอบสิทธิ์คำสั่งเหล่านั้นให้แก่เพลเยอร์ เซสชันจะจัดการคำสั่งการเล่นและเพลย์ลิสต์ที่กำหนดไว้ในอินเทอร์เฟซ 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 ที่มี MediaSessionService Intent Filter และขอสิทธิ์ 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()

การเผยแพร่การแจ้งเตือน

บริการที่ทำงานอยู่เบื้องหน้าต้องเผยแพร่การแจ้งเตือนขณะที่ทำงานอยู่ A 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 พร้อมสัญญาณเพิ่มเติมเกี่ยวกับประเภทของแผนผังเนื้อหาที่แอปไคลเอ็นต์ สนใจ