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

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

این صفحه شما را با برخی از مراحل کلیدی ساخت یک برنامه پخش موسیقی آشنا می‌کند و برای جزئیات بیشتر می‌توانید به راهنماهای کامل ما در مورد 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، مانند exoplayer-dash برای پخش استریم‌ها با فرمت DASH، نیز نیاز داشته باشید.

مطمئن شوید که 1.8.0 با نسخه دلخواه خود از کتابخانه جایگزین می‌کنید. برای مشاهده آخرین نسخه می‌توانید به یادداشت‌های انتشار مراجعه کنید.

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

با 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() در سطح API 24 و بالاتر یا متد چرخه عمر onResume() در سطح API 23 و پایین‌تر است. برای پخش‌کننده‌ای که در یک Service است، می‌توانید آن را در onCreate() آماده کنید. برای مثالی از نحوه پیاده‌سازی متدهای چرخه عمر، به codelab Exoplayer مراجعه کنید.

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

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

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

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

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

اگر بازیکن شما در یک Activity یا Fragment است، بازیکن را در متد onStop() lifecycle در سطح API 24 و بالاتر یا در متد onPause() در سطح API 23 و پایین‌تر آزاد کنید. برای بازیکنی که در یک Service است، می‌توانید آن را در onDestroy() آزاد کنید. برای مثالی از نحوه پیاده‌سازی متدهای lifecycle به codelab Exoplayer مراجعه کنید.

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

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

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

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

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

شما می‌توانید پس از مقداردهی اولیه یک پخش‌کننده، به صورت زیر یک 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 خود، یک شیء callback تنظیم کنید.

وقتی یک کنترلر می‌خواهد به جلسه رسانه شما متصل شود، متد 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 یا با ایجاد یک پیاده‌سازی سفارشی از رابط provider ایجاد کنید. provider خود را با setMediaNotificationProvider به MediaSession خود اضافه کنید.

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

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

پیاده‌سازی یک MediaLibraryService مشابه پیاده‌سازی MediaSessionService است، با این تفاوت که در onGetSession() باید به جای MediaSession یک MediaLibrarySession برگردانید. در مقایسه با 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" />

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

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

ایجاد یک MediaLibrarySession

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

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

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