Подключение 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.