Подключение exoplayer с MediaSessionCompat

Я хочу связать мою реализацию exoplayer с объектом медиа-сессии. Я настроил SimpleExoPlayerView, чтобы показать видео. Каждый раз, когда нажимается кнопка, я хочу, чтобы обратные вызовы сеанса мультимедиа срабатывали. Я могу получить обратные вызовы только тогда, когда используется что-то вроде пары наушников. Код, используемый в приложении, написан ниже

@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void setUp(LifecycleOwner lifecycleOwner){
    // Create a MediaSessionCompat
    Log.i("Hoe8", "lco setup called");
    mMediaSession = new MediaSessionCompat(activity, "this");

    // Enable callbacks from MediaButtons and TransportControls
    mMediaSession.setFlags(
            MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

    // Do not let MediaButtons restart the player when the app is not visible
    mMediaSession.setMediaButtonReceiver(null);

    // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
    mStateBuilder = new PlaybackStateCompat.Builder()
            .setActions(
                    PlaybackStateCompat.ACTION_PLAY |
                            PlaybackStateCompat.ACTION_PLAY_PAUSE);
    mMediaSession.setPlaybackState(mStateBuilder.build());

    // MySessionCallback has methods that handle callbacks from a media controller
    mMediaSession.setCallback(new MediaSessionCompat.Callback() {
        @Override
        public void onPlay() {
            super.onPlay();
            Log.i("Hoe8", "MediaSession callback play called");
            mMediaSession.setActive(true);
            ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(true);
            ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(true);

        }

        @Override
        public void onPause() {
            super.onPause();
            ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false);
        }

        @Override
        public void onStop() {
            super.onStop();
            mMediaSession.setActive(false);
            ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false);
            ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(false);
        }
    });

    // Create a MediaControllerCompat
    MediaControllerCompat mediaController =
            new MediaControllerCompat(activity, mMediaSession);

    MediaControllerCompat.setMediaController(activity, mediaController);

    //Handler mainHandler = new Handler();
    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory =
            new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector =
            new DefaultTrackSelector(videoTrackSelectionFactory);




// 2. Create the player
        player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector);
        playerView.setPlayer(player);

    MediaSessionConnector mediaSessionConnector =
            new MediaSessionConnector(mMediaSession);
    mediaSessionConnector.setPlayer(player, null,null );


}

Внесены некоторые изменения в код

public class VideoLifeCyclerObserver implements LifecycleObserver {

MediaSessionCompat mMediaSession;
PlaybackStateCompat.Builder mStateBuilder;
AppCompatActivity activity;
SimpleExoPlayerView playerView;
SimpleExoPlayer player;
ExoPlayer.ExoPlayerComponent rv;
MediaSessionConnector mediaSessionConnector;

public VideoLifeCyclerObserver(AppCompatActivity activity, SimpleExoPlayerView playerView, ExoPlayer.ExoPlayerComponent rv){
    this.activity = activity;
    this.playerView = playerView;
    this.activity.getLifecycle().addObserver(this);
    this.rv = rv;
    Log.i("Hoe8","video lco created");
}


@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void setUp(LifecycleOwner lifecycleOwner){
    // Create a MediaSessionCompat
    Log.i("Hoe8", "lco setup called");
    mMediaSession = new MediaSessionCompat(activity, "this");

    // Create a MediaControllerCompat
    MediaControllerCompat mediaController =
            new MediaControllerCompat(activity, mMediaSession);

    MediaControllerCompat.setMediaController(activity, mediaController);

    mediaSessionConnector =
            new MediaSessionConnector(mMediaSession, new PlayBackController());
}

@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void startPlayer(LifecycleOwner lifecycleOwner){
    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory =
            new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector =
            new DefaultTrackSelector(videoTrackSelectionFactory);
    player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector);
    playerView.setPlayer(player);
    mediaSessionConnector.setPlayer(player, null,null );
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void tearDown(LifecycleOwner lifecycleOwner){
    player.stop();
    player.release();
    player.sendMessages(new ExoPlayer.ExoPlayerMessage(rv,1,player.getContentPosition()));
}

public class PlayBackController extends DefaultPlaybackController{
    @Override
    public void onPause(Player player) {
        Log.i("Hoe8", "onPause called");
        ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false);
        super.onPause(player);
    }

    @Override
    public void onPlay(Player player) {
        Log.i("Hoe8", "MediaSession callback play called 2");
        mMediaSession.setActive(true);
        ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(true);
        ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(true);
        super.onPlay(player);
    }

    @Override
    public void onStop(Player player) {
        Log.i("Hoe8", "onStop called");
        mMediaSession.setActive(false);
        ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false);
        ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(false);
        super.onStop(player);
    }


}
}

Как я могу получить кнопки, которые отображаются в SimpleExoPlayerView для запуска обратных вызовов мультимедийного сеанса?

1 ответ

Решение

Короче:

удалить весь код в вашем onCreate, начиная с (включительно)

// Включить обратные вызовы из MediaButtons и TransportControls

в (эксклюзив)

// Создать MediaControllerCompat

:)

Более длительный:

Я рекомендую запускать обратные вызовы медиа-сессий, прослушивая изменения состояния проигрывателя, а не нажимая на кнопки. Это избавляет вас от этого для каждого элемента пользовательского интерфейса, взаимодействующего с игроком. Это на самом деле то, что MediaSessionConnector делает для вас.

С MediaSessionConnector вам не нужно манипулировать MediaSession самостоятельно. Соединитель является посредником между экземпляром проигрывателя и мультимедийным сеансом. Это означает, что соединитель прослушивает переходы состояния проигрывателя и отображает состояние проигрывателя в состояние сеанса мультимедиа. Соединитель также прослушивает мультимедийные действия, отправленные элементами управления транспортом, и передает их проигрывателю или вашему приложению. Примечание. Ваше приложение не должно предоставлять MediaSessionCompat.Callback, коннектор регистрирует свой собственный (и переопределяет ваш, так как может быть только один за сеанс).

В целом: ваше приложение взаимодействует только с экземпляром SimpleExoPlayer, в то время как соединитель отображает состояние проигрывателя в сеансе.

Давайте начнем с базового подхода, который отображает состояние проигрывателя на сеанс, который вызывает соответствующие методы MediaControllerCompat.Callback:

// code running in a activity or service where (this instanceof Context)

mediaSession = new MediaSessionCompat(this, getPackageName());
mediaSessionConnector = new MediaSessionConnector(mediaSession)
mediaSessionConnector.setPlayer(player, null, null);
mediaSession.setActive(true);

Теперь вы можете подготовить и использовать проигрыватель, как и раньше, например, вызвать setPlayWhenReady(true|false), seekTo(t), а соединитель поддерживает PlaybackStateCompat, который транслируется на контроллеры сеанса.

Соединитель получает и реализует несколько мультимедийных действий на этом уровне (нет необходимости в собственном MediaSession.Callback):

PlaybackStateCompat.ACTION_PLAY_PAUSE |
PlaybackStateCompat.ACTION_PLAY | 
PlaybackStateCompat.ACTION_PAUSE | 
PlaybackStateCompat.ACTION_STOP |
PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE |
PlaybackStateCompat.ACTION_SET_REPEAT_MODE;

PlayFromXYZ действия

Возможно, вы захотите поддержать дополнительные мультимедийные действия, такие как ACTION_PLAY_FROM_MEDIA_ID. Вы можете сделать это, предоставив свой PlaybackPreparer:

playbackPreparer = new YourPlaybackPreparer(); 
mediaSessionConnector.setPlayer(player, playbackPreparer, null);

Соединитель теперь делегирует действия, такие как ACTION_PLAY_FROM_MEDIA_ID или ACTION_PREPARE_FROM_MEDIA_ID, вашему подготовителю воспроизведения, который создает MediaSource для данного идентификатора мультимедиа для подготовки проигрывателя.

Управление метаданными и очередями

Также интересна возможность привязать временную шкалу проигрывателя непосредственно к очереди и метаданным медиа-сессии. Для этого вы можете предоставить QueueNavigator. Существует абстрактный TimelineQueueNavigator, предоставляемый расширением:

QueueNavigator queueNavigator = new TimelineQueueNavigator(mediaSession) {
  @Override
  public MediaDescriptionCompat getMediaDescription(int windowIndex) {
    // implement this method and read from your backing data:
    getMediaDescriptionAtQueuePosition(windowIndex):
    return mediaDescription;
  }
}
mediaSessionConnector.setQueueNavigator(queueNavigator);

Благодаря этому медиа-контроллеры теперь могут читать метаданные и очередь сеанса. Очередь представляет текущую временную шкалу игрока, а метаданные сессии описывают окно на временной шкале, которое воспроизводится в данный момент. ( подробнее о плейлистах).

При наличии TimelineQueueNavigator соединитель прослушивает ACTION_SKIP_TO_NEXT, ACTION_SKIP_TO_PREVIOUS и ACTION_SKIP_TO_QUEUE_ITEM, отправленные элементами управления транспортом, и соответственно перемещается по временной шкале.

Интеграция жизненного цикла

Обратите внимание, что вы должны создать экземпляр проигрывателя onStart/onResume и выпустить его onPause/onStop. Это гарантирует, что ресурсы кодека, которыми вы делитесь с другими приложениями, освобождаются, когда вы находитесь в фоновом режиме. Ваш пример кода делает это только один раз при создании, что не является хорошим гражданством:). Посмотрите, как это делает демонстрационное приложение ExoPlayer.

Также обратите внимание на средний блог о MediaSessionConnector.

Другие вопросы по тегам