ایجاد کردن برنامه پخش‌کننده رسانه پایه بااستفاده از Media3 ExoPlayer

‫Jetpack Media3 یک میانای Player تعریف می‌کند که عملکرد پایه را برای بازپخش فایل‌های ویدیویی و صوتی مشخص می‌کند. ‫ExoPlayer پیاده‌سازی پیش‌فرض این میانای Media3 است. توصیه می‌کنیم از ExoPlayer استفاده کنید، زیرا مجموعه جامعی از ویژگی‌ها را ارائه می‌دهد که اکثر موارد استفاده از بازپخش را پوشش می‌دهد و برای رسیدگی به هر مورد استفاده دیگری که ممکن است داشته باشید قابل‌سفارشی‌سازی است. ExoPlayer همچنین تکه‌تکه‌سازی سیستم‌عامل و دستگاه را انتزاعی می‌کند تا کد شما به‌طور مداوم در سراسر اکوسیستم Android کار کند. ExoPlayer شامل موارد زیر است:

این صفحه شما را با برخی‌از مراحل کلیدی در ساختن برنامه بازپخش آشنا می‌کند، و برای جزئیات بیشتر می‌توانید به راهنماهای کامل ما درباره Media3 ExoPlayer مراجعه کنید.

شروع کردن

برای شروع، وابستگی به ExoPlayer،‏ UI، و Common modules از Jetpack Media3 را اضافه کنید:

کاتلین

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 نیز نیاز داشته باشید، مانند exoplayer-dash برای پخش جریانی در قالب DASH.

حتماً 1.11.1 را با نسخه ترجیحی‌تان از کتابخانه جایگزین کنید. برای دیدن جدیدترین نسخه، می‌توانید به یادداشت‌های انتشار مراجعه کنید.

درحال ایجاد پخش‌کننده رسانه

با Media3، می‌توانید از پیاده‌سازی گنجانده‌شده میانای Player، ExoPlayer، استفاده کنید یا پیاده‌سازی سفارشی خودتان را بسازید.

ایجاد ExoPlayer

ساده‌ترین راه برای ایجاد نمونه ExoPlayer به شرح زیر است:

کاتلین

val player = ExoPlayer.Builder(context).build()

جاوا

ExoPlayer player = new ExoPlayer.Builder(context).build();

می‌توانید پخش‌کننده رسانه خود را در روش چرخه حیات onCreate() Activity، Fragment، یا Service که در آن قرار دارد ایجاد کنید.

Builder شامل طیفی از گزینه‌های سفارشی‌سازی است که ممکن است به آن‌ها علاقه داشته باشید، ازجمله:

‫Media3 مؤلفه میانای کاربر PlayerView را ارائه می‌دهد که می‌توانید آن را در فایل چیدمان برنامه‌تان بگنجانید. این مؤلفه PlayerControlView را برای کنترل‌های بازپخش، SubtitleView را برای نمایش زیرنویس، و Surface را برای پرداز ویدیو دربرمی‌گیرد.

درحال آماده کردن پخش‌کننده

موارد رسانه‌ای را به فهرست پخش اضافه کنید تا با روش‌هایی مثل setMediaItem() و addMediaItem() بازپخش شوند. سپس، prepare() را فراخوانی کنید تا بار کردن رسانه شروع شود و منابع لازم را به‌دست آورید.

این مراحل را نباید قبل‌از اینکه برنامه در پیش‌زمینه قرار بگیرد انجام دهید. اگر پخش‌کننده شما در Activity یا Fragment باشد، این به‌معنای آماده‌سازی پخش‌کننده در روش چرخه حیات onStart() در سطح میانای برنامه‌سازی کاربردی ۲۴ و بالاتر یا روش چرخه حیات onResume() در سطح میانای برنامه‌سازی کاربردی ۲۳ و پایین‌تر است. برای بازیکنی که در Service است، می‌توانید آن را در onCreate() آماده کنید. برای دیدن نمونه‌ای از نحوه پیاده‌سازی روش‌های چرخه حیات، به Exoplayer codelab مراجعه کنید.

کنترل کردن پخش‌کننده

پس‌از آماده شدن پخش‌کننده، می‌توانید بازپخش را با فراخوانی روش‌ها در پخش‌کننده کنترل کنید، مانند:

مؤلفه‌های واسط کاربر مثل PlayerView یا PlayerControlView وقتی به پخش‌کننده متصل شوند، به‌روزرسانی خواهند شد.

بازیکن را رها کنید

بازپخش ممکن است به منابعی نیاز داشته باشد که عرضه محدودی دارند، مانند رمزگشاهای ویدیو، بنابراین مهم است که وقتی دیگر به پخش‌کننده نیاز ندارید، release() را در پخش‌کننده خود فراخوانی کنید تا منابع آزاد شوند.

اگر پخش‌کننده شما در Activity یا Fragment است، پخش‌کننده را در روش چرخه حیات onStop() در سطح میانای برنامه‌سازی کاربردی ۲۴ و بالاتر یا روش onPause() در سطح میانای برنامه‌سازی کاربردی ۲۳ و پایین‌تر منتشر کنید. برای بازیکنی که در Service است، می‌توانید آن را در onDestroy() منتشر کنید. برای نمونه‌ای از نحوه پیاده‌سازی روش‌های چرخه حیات، به Exoplayer codelab مراجعه کنید.

مدیریت بازپخش با جلسه رسانه

در Android، جلسه‌های رسانه روشی استاندارد برای تعامل با پخش‌کننده رسانه در مرزهای فرایند ارائه می‌دهند. اتصال جلسه رسانه به پخش‌کننده به شما امکان می‌دهد بازپخش رسانه خود را به‌صورت خارجی تبلیغ کنید و فرمان‌های بازپخش را از منابع خارجی دریافت کنید، برای مثال برای ادغام با کنترل‌های رسانه سیستم در دستگاه‌های همراه و صفحه‌نمایش بزرگ.

برای استفاده از جلسه‌های رسانه، وابستگی به ماژول Media3 Session را اضافه کنید:

کاتلین

implementation("androidx.media3:media3-session:1.11.1")

شیک

implementation "androidx.media3:media3-session:1.11.1"

ایجاد جلسه رسانه

می‌توانید پس‌از راه‌اندازی پخش‌کننده، MediaSession را به‌صورت زیر ایجاد کنید:

کاتلین

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

جاوا

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() نمونه‌سازی کند.

کاتلین

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()
    }
}

جاوا

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();
    }
}

در مانیفست، کلاس Service را با فیلتر هدف 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 را برگردانید.

کاتلین

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

جاوا

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

درحال اتصال به واسط کاربر شما

اکنون که جلسه رسانه شما در Service جدا از Activity یا Fragment که واسط کاربر پخش‌کننده شما در آن قرار دارد، می‌توانید از MediaController برای پیوند دادن آن‌ها به یکدیگر استفاده کنید. در روش onStart() از Activity یا Fragment با میانای کاربر، SessionToken برای MediaSession ایجاد کنید، سپس از SessionToken برای ساختن MediaController استفاده کنید. ساختن MediaController به‌صورت ناهمگام اتفاق می‌افتد.

کاتلین

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()
  )
}

جاوا

@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 یا با ایجاد پیاده‌سازی سفارشی میانای ارائه‌دهنده ایجاد کنید. ارائه‌دهنده‌تان را با setMediaNotificationProvider به MediaSession اضافه کنید.

تبلیغ کتابخانه محتوای شما

MediaLibraryService براساس MediaSessionService ساخته می‌شود و به برنامه‌های کارخواه اجازه می‌دهد محتوای رسانه‌ای ارائه‌شده توسط برنامه شما را مرور کنند. برنامه‌های کارخواه برای تعامل با MediaLibraryService شما، MediaBrowser را پیاده‌سازی می‌کنند.

پیاده‌سازی MediaLibraryService شبیه به پیاده‌سازی MediaSessionService است، با این تفاوت که در onGetSession() باید MediaLibrarySession را به‌جای MediaSession برگردانید. درمقایسه با MediaSession.Callback، MediaLibrarySession.Callback شامل روش‌های اضافی است که به کارخواه مرورگر اجازه می‌دهد در محتوای ارائه‌شده توسط سرویس کتابخانه شما پیمایش کند.

مشابه MediaSessionService، MediaLibraryService را در مانیفست خود اعلام کنید و برای اجرای سرویس پیش‌زمینه‌ای، اجازه 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" />

نمونه بالا شامل فیلتر قصد برای هر دو MediaLibraryService و، برای سازگاری با نسخه قدیمی، MediaBrowserService قدیمی است. فیلتر قصد اضافی به برنامه‌های کارخواه که از میانای برنامه‌سازی کاربردی MediaBrowserCompat استفاده می‌کنند امکان می‌دهد Service شما را تشخیص دهند.

MediaLibrarySession به شما امکان می‌دهد کتابخانه محتوایتان را در ساختار درختی، با یک ریشه MediaItem ارائه دهید. هر MediaItem در درخت می‌تواند هر تعداد گره فرزند MediaItem داشته باشد. می‌توانید براساس درخواست برنامه کارخواه، ریشه متفاوت یا درخت متفاوتی را ارائه دهید. برای مثال، درختی که به مشتری‌ای که به‌دنبال فهرستی از موارد رسانه‌ای توصیه‌شده است برمی‌گردانید ممکن است فقط حاوی ریشه MediaItem و یک سطح از گره‌های فرزند MediaItem باشد، درحالی‌که درختی که به برنامه مشتری دیگری برمی‌گردانید ممکن است نشان‌دهنده کتابخانه کامل‌تری از محتوا باشد.

درحال ایجاد کردن MediaLibrarySession

MediaLibrarySession میانای برنامه‌سازی کاربردی MediaSession را گسترش می‌دهد تا میاناهای برنامه‌سازی کاربردی مرور محتوا را اضافه کند. درمقایسه با فراخوانی MediaSession، فراخوانی MediaLibrarySession روش‌هایی مانند موارد زیر را اضافه می‌کند:

  • onGetLibraryRoot() برای زمانی که کارخواه ریشه MediaItem درخت محتوا را درخواست می‌کند
  • onGetChildren() برای زمانی که مشتری فرزندان MediaItem را در درخت محتوا درخواست می‌کند
  • onGetSearchResult() برای زمانی که مشتری نتایج جستجو را از درخت محتوا برای پُرسمان داده‌شده درخواست می‌کند

روش‌های فراخوانی مربوطه شامل شیء LibraryParams با نشان‌های اضافی درباره نوع درخت محتوایی است که برنامه مشتری به آن علاقه دارد.