android - 将 exoplayer 与 MediaSessionCompat 连接

标签 android exoplayer android-mediasession

我想将我的 exoplayer 实现与媒体 session 对象连接起来。我设置了一个 SimpleExoPlayerView 来显示视频。每次单击按钮时,我都希望触发媒体 session 回调。我只能在使用耳机之类的东西时触发回调。 App中使用的代码写在下面

@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 中显示的按钮触发媒体 session 回调?

最佳答案

简而言之:

从(含)开始删除 onCreate 中的所有代码

//启用来自 MediaButtons 和 TransportControls 的回调

到(不包括)

//创建一个 MediaControllerCompat

:)

更冗长:

我建议通过监听播放器的状态转换而不是通过点击按钮来触发媒体 session 回调。这使您无需为与播放器交互的每个 UI 元素执行此操作。这实际上就是 MediaSessionConnector 为您所做的。

使用 MediaSessionConnector,您无需自己操作 MediaSession。连接器在播放器实例和媒体 session 之间进行调解。这意味着连接器会监听播放器的状态转换并将播放器状态映射到媒体 session 状态。连接器还监听传输控件发送的媒体操作,并将它们委托(delegate)给播放器或您的应用程序。注意:您的应用不需要提供 MediaSessionCompat.Callback,连接器会自行注册(并覆盖您的,因为每个 session 只能有一个)。

通常:您的应用仅与 SimpleExoPlayer 实例交互,而连接器将播放器状态映射到 session 。

让我们从基本方法开始,它将播放器的状态映射到触发适当的 MediaControllerCompat.Callback 方法的 session :

// 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) 并且连接器维护广播到 session Controller 的 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 等操作委托(delegate)给您的播放准备器,它为给定的媒体 ID 创建一个 MediaSource 以准备播放器。

元数据和队列管理

同样有趣的是能够将播放器的时间轴直接映射到媒体 session 的队列和元数据。为此,您可以提供 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);

有了这个媒体 Controller 现在可以读取 session 的元数据和队列。队列表示播放器的当前时间轴, session 的元数据描述当前正在播放的时间轴中的窗口。 (more about playlists)。

提供一个 TimelineQueueNavigator,连接器监听传输控件发送的 ACTION_SKIP_TO_NEXT、ACTION_SKIP_TO_PREVIOUS 和 ACTION_SKIP_TO_QUEUE_ITEM,并相应地沿着时间线导航。

生命周期集成

请注意,您必须在onStart/onResume 上创建播放器实例并在onPause/onStop 上释放它。这可确保您与其他应用程序共享的编解码器资源在您处于后台时得到释放。您的代码示例只在 onCreate 上执行一次,这不是好的公民:)。 See how the ExoPlayer demo app does it .

请同时考虑 Medium blog about the MediaSessionConnector .

关于android - 将 exoplayer 与 MediaSessionCompat 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48046882/

相关文章:

android - 如何获取微信授权 token ?

android - 如何使用 Native Android 制作一个视频播放器,它具有我们在 YouTube 中看到的分辨率切换能力?

android - 单击耳机按钮时应用程序崩溃

android - GridView - 第一个和最后一个位置的第一个项目

android - 从 Phonebook Android 获取联系人时如何过滤 CONTACT_LAST_UPDATED_TIMESTAMP 上的联系人?

android - 在特定时间将 imageview 从位置 A 移动到 B?

android - 在 Android 上下载流媒体视频

Android - 具有音频焦点的 SimpleExoPlayer

android - 第二次调用 setActive(true) 时,App 不会成为首选的 MediaButtonReceiver

android - 如何让Android MediaSession在显示进度时考虑播放速度