کنترل رسانه از طریق MediaSession

۱. مقدمه

آخرین به‌روزرسانی: 2020-09-09

مزایای اضافه کردن MediaSession در پخش ویدیو چیست؟

جلسات رسانه‌ای (Media Sessions) یک پیوند جدایی‌ناپذیر بین پلتفرم اندروید و برنامه‌های رسانه‌ای هستند. این جلسات نه تنها اندروید را از پخش رسانه مطلع می‌کنند - تا بتواند اقدامات رسانه‌ای را به جلسه صحیح هدایت کند - بلکه به پلتفرم نیز اطلاع می‌دهند که چه چیزی در حال پخش است و چگونه می‌توان آن را کنترل کرد.

نمایش یک MediaSession از طریق برنامه شما مزایای مختلفی دارد که کاربران از آن لذت خواهند برد. در اینجا چند مثال عالی آورده شده است.

دستیار گوگل

کاربران می‌توانند به راحتی از طریق دستورات صوتی مانند «مکث»، «ادامه» و «بعدی» با رسانه‌های موجود در برنامه شما تعامل داشته باشند. همچنین می‌توان از متادیتای رسانه‌های شما برای دریافت پاسخ در مورد آنچه در حال پخش است، استفاده کرد.

تلویزیون اندروید

در تجربه‌های صفحه نمایش بزرگ، برنامه تلویزیون اندروید شما می‌تواند از کنترل‌های از راه دور معمولی برای کاربرانی که تلویزیون‌هایشان از HDMI-CEC پشتیبانی می‌کنند، استفاده کند. دستورات صادر شده توسط دکمه‌های پخش/مکث، توقف، بعدی و قبلی به برنامه شما منتقل می‌شوند.

کنترل‌های رسانه‌ای روی صفحه

با شروع از اندروید ۴.۰ (سطح API ۱۴)، سیستم می‌تواند به وضعیت پخش و فراداده‌های یک جلسه رسانه دسترسی داشته باشد. این قابلیت به صفحه قفل امکان نمایش کنترل‌های رسانه و آثار هنری را می‌دهد. این رفتار بسته به نسخه اندروید متفاوت است.

رسانه‌های پس‌زمینه

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

محاسبات محیطی

ارائه اطلاعات مربوط به محتوای در حال پخش و نحوه کنترل آن به رسانه شما می‌تواند پلی بین دستگاه‌ها باشد تا کاربران بتوانند به روش‌های مختلفی که از آن لذت می‌برند با آن تعامل داشته باشند.

آنچه خواهید ساخت

در این آزمایشگاه کد، شما قصد دارید نمونه Exoplayer موجود را برای افزودن پشتیبانی از جلسه رسانه‌ای گسترش دهید. برنامه شما:

  • وضعیت فعال جلسه رسانه‌ای را به درستی منعکس کنید
  • کنترل‌های رسانه را به ExoPlayer منتقل کنید
  • ارسال فراداده‌های اقلام موجود در صف به جلسه رسانه

آنچه یاد خواهید گرفت

  • چرا جلسات رسانه‌ای تجربه غنی‌تری را به کاربران ارائه می‌دهند؟
  • نحوه ایجاد یک جلسه رسانه‌ای و مدیریت وضعیت آن
  • نحوه اتصال یک جلسه رسانه‌ای به ExoPlayer
  • نحوه‌ی گنجاندن فراداده‌ی موارد موجود در صف پخش در جلسه‌ی رسانه
  • نحوه اضافه کردن اقدامات اضافی (سفارشی)

این آزمایشگاه کد بر روی MediaSession SDK تمرکز دارد. مفاهیم و بلوک‌های کد غیرمرتبط، از جمله جزئیات مربوط به پیاده‌سازی ExoPlayer، مورد بحث قرار نمی‌گیرند، اما برای کپی و پیست ساده شما ارائه شده‌اند.

آنچه نیاز دارید

  • نسخه جدید اندروید استودیو (۳.۵ یا بالاتر)
  • آشنایی اولیه با توسعه اپلیکیشن‌های اندروید

۲. راه‌اندازی

نقطه شروع ما کجاست؟

نقطه شروع ما دموی اصلی ExoPlayer است. این دمو شامل ویدیوهایی با کنترل‌های پخش روی صفحه است، اما از جلسات رسانه‌ای از پیش تعیین‌شده استفاده نمی‌کند. این مکان بسیار خوبی برای ماست که به سراغ آنها برویم و آنها را اضافه کنیم!

نمونه ExoPlayer را دریافت کنید

برای شروع، بیایید با نمونه ExoPlayer شروع کنیم. با اجرای کد زیر، مخزن GitHub را کلون کنید.

git clone https://github.com/google/ExoPlayer.git

نسخه نمایشی را باز کنید

در اندروید استودیو، پروژه دموی اصلی که در مسیر demos/main قرار دارد را باز کنید.

اندروید استودیو از شما می‌خواهد مسیر SDK را تنظیم کنید. در صورت بروز هرگونه مشکل، می‌توانید توصیه‌های مربوط به به‌روزرسانی ابزارهای IDE و SDK را دنبال کنید.

10e3b5c652186d57.png

اگر از شما خواسته شد که از آخرین نسخه Gradle استفاده کنید، آن را به‌روزرسانی کنید.

کمی وقت بگذارید تا درک اولیه‌ای از نحوه طراحی برنامه به دست آورید. توجه داشته باشید که دو فعالیت وجود دارد: SampleChooserActivity و PlayerActivity. ما بقیه کد را در PlayerActivity، جایی که رسانه در واقع پخش می‌شود، خواهیم گذراند، بنابراین این کلاس را باز کنید و به بخش بعدی بروید.

۳. یک جلسه رسانه‌ای ایجاد کنید و وضعیت آن را مدیریت کنید

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

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

دو فیلد زیر را در بالای کلاس تعریف کنید. ما در سراسر این بخش از این فیلدها استفاده خواهیم کرد.

private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;

شما باید وابستگی پروژه "extension-mediasession" را به build.gradle در سطح ماژول برای "Module: demo" اضافه کنید:

implementation project(path: ':extension-mediasession')

توجه داشته باشید که اگر ماوس را روی خطای مربوط به حل MediaSessionConnector قرار دهید، اندروید استودیو می‌تواند در اضافه کردن خودکار این وابستگی به شما کمک کند:

60055e4ad54fbb97.png

در نهایت، با اضافه کردن موارد زیر، مشکل import کلاس‌ها را حل کنید:

import android.support.v4.media.session.MediaSessionCompat;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;

وقتی اکتیویتی ایجاد شد، می‌خواهیم یک جلسه رسانه‌ای و یک رابط جلسه رسانه‌ای ایجاد کنیم که به عنوان واسطه بین جلسه رسانه‌ای و ExoPlayer عمل می‌کند.

مکان ایده‌آل برای درج این کد، جایی است که ExoPlayer نیز ایجاد می‌شود. در برنامه آزمایشی ما، می‌توانیم کد خود را به انتهای initializePlayer() اضافه کنیم. حتماً این منطق را پس از نمونه‌سازی پخش‌کننده اضافه کنید!

private void initializePlayer() {
  if (player == null) {
    ...
    player = ...
    ...
    mediaSession = new MediaSessionCompat(this, "sample");
    mediaSessionConnector = new MediaSessionConnector(mediaSession);
    mediaSessionConnector.setPlayer(player);
  }
  ...
}

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

وقتی دیگر نیازی به جلسه رسانه نیست، آن را آزاد کنید. وقتی ExoPlayer را در releasePlayer() آزاد می‌کنیم، می‌توانیم کد زیر را نیز برای این کار وارد کنیم:

private void releasePlayer() {
  if (mediaSession != null) {
    mediaSession.release();
  }
  ...
}

مدیریت وضعیت جلسه رسانه

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

وقتی کاربر فعالیت را شروع می‌کند، جلسه رسانه باید فعال شود:

@Override
public void onStart() {
  ...
  if (mediaSession != null) {
    mediaSession.setActive(true);
  }
}

از آنجا که برنامه ما در پس‌زمینه رسانه پخش نمی‌کند، ضروری است که اطمینان حاصل شود که جلسه رسانه با خروج کاربر از فعالیت غیرفعال می‌شود:

@Override
public void onStop() {
  super.onStop();
  if (mediaSession != null) {
    mediaSession.setActive(false);
  }
  ...
}

بیایید نسخه آزمایشی را اجرا کنیم

  1. یک دستگاه اندروید وصل کنید یا یک شبیه‌ساز را اجرا کنید.
  2. مطمئن شوید که گزینه "demo" برای اجرا از نوار ابزار اندروید استودیو انتخاب شده است. cb1ec4e50886874f.png
  3. کلیک 9d8fb3a9ddf12827.png از نوار ابزار اندروید استودیو.
  4. پس از اجرای برنامه روی دستگاهتان، یک پخش جریانی ویدیویی را برای پخش انتخاب کنید.
  5. پس از شروع پخش، با استفاده از دستورات adb زیر، جلسه رسانه را کنترل کنید:
adb shell media dispatch pause
adb shell media dispatch play
adb shell media dispatch play-pause
adb shell media dispatch fast-forward
adb shell media dispatch rewind
  1. همچنین بررسی کنید که اندروید چگونه جلسه رسانه شما را می‌بیند. به طور خاص، می‌توانید با نگاه کردن به فیلد اکشن، بررسی کنید که کدام اکشن‌ها پشتیبانی می‌شوند. عددی که در اینجا می‌بینید ترکیبی از شناسه‌های اکشن است، همانطور که در شیء PlaybackState اعلام شده است. برای دیدن جلسه رسانه، دستور زیر را اجرا کنید: adb shell dumpsys media_session
  2. اگر از یک دستگاه فیزیکی دارای میکروفون استفاده می‌کنید، سعی کنید دستیار گوگل را فراخوانی کرده و دستورات صوتی مانند «مکث»، «ادامه»، «یک دقیقه به جلو» را صادر کنید.

b8dda02a6fb0f6a4.png نمونه ExoPlayer که روی اندروید تی‌وی اجرا می‌شود.

۴. گنجاندن فراداده‌های موارد موجود در صف پخش

اکنون می‌توانیم ویژگی‌های پشتیبانی‌شده‌ی جلسه‌ی رسانه‌ای خود را که قبلاً MediaSessionConnector خود را در initializePlayer() ایجاد کرده بودیم، گسترش دهیم.

افزودن یک TimelineQueueNavigator

ExoPlayer ساختار رسانه را به صورت یک جدول زمانی نشان می‌دهد. برای جزئیات بیشتر در مورد نحوه‌ی کار این قابلیت، کمی وقت بگذارید و در مورد شیء Timeline در ExoPlayer مطالعه کنید. با استفاده از این ساختار، می‌توانیم از تغییرات محتوا مطلع شویم و در صورت درخواست، فراداده‌ی آنچه در حال پخش است را نمایش دهیم.

برای انجام این کار، یک TimelineQueueNavigator تعریف خواهیم کرد. نمونه MediaSessionConnector را در initializePlayer() پیدا کنید و پس از مقداردهی اولیه mediaSession ، یک پیاده‌سازی از TimelineQueueNavigator اضافه کنید.

mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
  @Override
  public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
    return new MediaDescriptionCompat.Builder()
            .setTitle(player.getCurrentMediaItem().mediaMetadata.title)
            .setDescription("MediaDescription description for " + windowIndex)
            .setSubtitle("MediaDescription subtitle")
            .build();
  }
});

مشکل import کلاس‌ها را با اضافه کردن موارد زیر حل کنید:

import android.support.v4.media.MediaDescriptionCompat;
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;

مشاهده کنید که پارامتر windowIndex با آیتم آن اندیس در صف پخش مطابقت دارد.

حالا که مقداری متادیتا اضافه کرده‌اید، می‌توانید آزمایش کنید که دستیار (Assistant) متوجه می‌شود چه چیزی در حال پخش است. هنگام پخش یک ویدیو در اندروید تی‌وی، دستیار را فراخوانی کنید و بپرسید «چه چیزی در حال پخش است؟»

6c7fc0cb853cbc38.png

۵. سفارشی‌سازی اقدامات

شاید پخش‌کننده شما از برخی اقدامات پشتیبانی نمی‌کند، یا می‌خواهید پشتیبانی از اقدامات بیشتری را در آن بگنجانید؟ اکنون بیایید کمی عمیق‌تر به جلسه رسانه‌ای بپردازیم که قبلاً MediaSessionConnector خود را در initializePlayer() ایجاد کرده بودیم.

اعلام اقدامات پشتیبانی شده

برای سفارشی‌سازی اکشن‌هایی که می‌خواهید جلسه رسانه از آنها پشتیبانی کند، از mediaSessionConnector.setEnabledPlaybackActions() استفاده کنید.

توجه داشته باشید که مجموعه کامل شامل موارد زیر است:

mediaSessionConnector.setEnabledPlaybackActions(
        PlaybackStateCompat.ACTION_PLAY_PAUSE
                | PlaybackStateCompat.ACTION_PLAY
                | PlaybackStateCompat.ACTION_PAUSE
                | PlaybackStateCompat.ACTION_SEEK_TO
                | PlaybackStateCompat.ACTION_FAST_FORWARD
                | PlaybackStateCompat.ACTION_REWIND
                | PlaybackStateCompat.ACTION_STOP
                | PlaybackStateCompat.ACTION_SET_REPEAT_MODE
                | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
);

بیایید دوباره ببینیم که این داده‌ها چگونه در معرض پلتفرم قرار می‌گیرند:

  1. مانند قبل، یک ویدیو را شروع کنید.
  2. با اجرای دستور adb shell dumpsys media_session ، نحوه‌ی مشاهده‌ی متادیتای جلسه‌ی رسانه‌ای توسط اندروید را بررسی کنید.
  3. خطی که شامل فراداده است را پیدا کنید و مشاهده کنید که عنوان و توضیحات در آن گنجانده شده و با com.google.android.exoplayer2.demo/sample مرتبط هستند.

افزودن اقدامات اضافی

می‌توانیم جلسه رسانه‌ای خود را با برخی اقدامات اضافی گسترش دهیم. در این بخش، فقط پشتیبانی از زیرنویس‌ها را اضافه خواهیم کرد.

زیرنویس‌های پشتیبان

افزودن پشتیبانی از زیرنویس به جلسات رسانه‌ای به کاربران این امکان را می‌دهد که آنها را با صدا تغییر دهند. در جایی که رابط جلسه رسانه را مقداردهی اولیه کرده‌اید، موارد زیر را اضافه کنید:

mediaSessionConnector.setCaptionCallback(new MediaSessionConnector.CaptionCallback() {
      @Override
      public void onSetCaptioningEnabled(Player player, boolean enabled) {
        Log.d("MediaSession", "onSetCaptioningEnabled: enabled=" + enabled);
      }

      @Override
      public boolean hasCaptions(Player player) {
        return true;
      }

      @Override
      public boolean onCommand(Player player, ControlDispatcher controlDispatcher, String command, Bundle extras, ResultReceiver cb) {
        return false;
      }
    }
);

در نهایت، هرگونه واردات از دست رفته را برطرف کنید.

می‌توانید این را با فراخوانی دستیار گوگل در اندروید تی‌وی و گفتن «فعال کردن زیرنویس‌ها» آزمایش کنید. برای مشاهده‌ی نحوه‌ی فراخوانی این کد، پیام‌های Logcat را بررسی کنید.

۶. تبریک

تبریک می‌گویم، شما با موفقیت جلسات رسانه‌ای را به نمونه اضافه کردید!

شما با انجام موارد زیر به قابلیت‌های فوق‌العاده‌ای دست یافته‌اید:

  • اضافه کردن یک جلسه رسانه‌ای،
  • اتصال جلسات رسانه‌ای به نمونه‌ای از ExPlayer،
  • افزودن فراداده و اقدامات اضافی.

اکنون مراحل کلیدی مورد نیاز برای غنی‌سازی یک برنامه رسانه‌ای و ارائه یک تجربه متنوع‌تر به کاربران را می‌دانید!

نکته پایانی

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

برای انجام این کار، به سادگی وابستگی‌های پروژه مانند موارد زیر را جایگزین کنید:

implementation project(modulePrefix + 'library-core')
implementation project(path: ':extension-mediasession')

برای دریافت از مخازن Maven، مانند:

implementation 'com.google.android.exoplayer:exoplayer-core:2.+'
implementation 'com.google.android.exoplayer:extension-mediasession:2.+'

اسناد مرجع