带有 ExoPlayer 和 PlayerControlView 的 MediaBrowserService - 如何从 UI (PlayerControlView) 访问播放器实例?

Posted

技术标签:

【中文标题】带有 ExoPlayer 和 PlayerControlView 的 MediaBrowserService - 如何从 UI (PlayerControlView) 访问播放器实例?【英文标题】:MediaBrowserService with ExoPlayer and PlayerControlView - How to access to the player instance from the UI (PlayerControlView)? 【发布时间】:2020-04-03 18:05:08 【问题描述】:

我目前正在开发具有视频和音频功能的应用程序的一部分,并且最近开始重构代码库。目标是整合MediaSession/ MediaControllerMediaBrowserService/ MediaBrowser框架。

我们使用ExoPlayerPlayerControlView 更具体地说,PlayerView 用于视频和音频组件,并且它需要对PlayerControlView 的播放器实例的引用:

/**
   * Sets the @link Player to control.
   *
   * @param player The @link Player to control, or @code null to detach the current player. Only
   *     players which are accessed on the main thread are supported (@code
   *     player.getApplicationLooper() == Looper.getMainLooper()).
   */
  public void setPlayer(@Nullable Player player) ...

但是,在android developers post 和MediaBrowserService 的documentation 下,播放器实例应该包含在服务下。此外,客户端站点(MediaBrowser 和 MediaController)通过connect() 方法和MediaBrowserConnectionCallback 与之对话的唯一方法,这使得将播放器的实例传递给 PlayerControlView(或其他方式)不会可能。

我尝试过使用各种回调,例如 MediaSessionCompat.Callback,但 SimpleExoPlayer 或 PlayerControlView 都不是 Parcelable。

在传统服务中,我们使用Binder 来访问我们在服务中声明的方法并执行以下操作:

boolean attachPlayerControlView(PlayerControlView playerControlView) 
            if (player != null) 
                playerControlView.setPlayer(player);
                return true;
            
            return false;
        

但是,MediaBrowserService/ MediaBrowser 框架似乎无法做到这一点。我检查了这个question的答案,这表明使用[sendCommand]是一种调用自定义方法的方式。但它也要求参数是 Parcelable。

总而言之,我的问题是,有没有办法让PlayerControlView 访问SimpleExoPlayer 的实例,或者在MediaBrowserService 框架下以其他方式访问。

非常感谢任何答案或cmets。

【问题讨论】:

你能做到吗? @WISHI 不,我们做不到。我们最终采用了两种不同的音频和视频方法。对于音频,我们将客户端站点与MediaBrowserService 分离,更具体地说,我们放弃了使用PlayerControlViewExoPlayer 的好处。这意味着我们必须自己做很多繁重的工作。对于视频,我们删除了MediaBrowserServiceMediaBrowser 的使用,因为这似乎没有必要,使用MediaSession / MediaController 就足够了。 【参考方案1】:

此外,客户端站点(MediaBrowser 和 MediaController)通过 connect() 方法和 MediaBrowserConnectionCallback 与之通信的唯一方法,这使得将播放器的实例传递给 PlayerControlView(或其他方式)不可能。

据我了解,这是不正确的。你总是可以绑定到 MediaBrowserService 以传统方式,即使用 IBinder 访问 Service。 (虽然我不确定这是否是正确的方法,否则我必须在 Service 中创建一个静态 MediaPlayer 实例。

我在使用 MediaPlayer 播放视频时遇到了类似的问题。我已经使用 IBinder 绑定到 MediaBrowserService,然后获取了 MediaPlayer 实例。在您的服务中提供一个返回对 MediaPlayer 的引用的方法。像这样的:

private MediaPlayer mediaPlayer;

@Override
public IBinder onBind(Intent intent) 
    if (intent.getAction().equals("YOUR_INTENT")) 
        return new LocalBinder();
    
    return super.onBind(intent);


public class LocalBinder extends Binder
    public Audioservice getService()
        return AudioService.this;
    


public MediaPlayer getMediaPlayer() 
    return mediaPlayer;

然后在您的 Activity/Fragment 中使用 IBinder 绑定到 MediaBrowserService。在我的实现中,我使用了 MediaPlayer,但我认为它可以以类似的方式用于 Exoplayer。

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback

private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private boolean isServiceBounded;
private boolean isSurfaceReady;

private ServiceConnection serviceConnection = new ServiceConnection() 
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) 
        isServiceBounded = true;
        mediaPlayer = ((AudioService)service).getMediaPlayer();
        if (isSurfaceReady) 
            mediaPlayer.setDisplay(surfaceHolder);
        
    

    @Override
    public void onServiceDisconnected(ComponentName name) 
        isServiceBounded = false;
    
;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    surfaceView = findViewById(R.id.surfaceView);
    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.addCallback(this);
    bindService(new Intent("YOUR_INTENT"), serviceConnection, BIND_AUTO_CREATE);


@Override
public void surfaceCreated(SurfaceHolder holder) 
    surfaceHolder = holder;
    isSurfaceReady = true;
    if (mediaPlayer != null) 
        mediaPlayer.setDisplay(holder);
    


@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 



@Override
public void surfaceDestroyed(SurfaceHolder holder) 
    isSurfaceReady = false;
    if (mediaPlayer != null) 
        mediaPlayer.setDisplay(null);
    
    surfaceHolder = null;


@Override
protected void onDestroy() 
    super.onDestroy();
    unbindService(serviceConnection);

【讨论】:

非常感谢阿伦!您是 100% 正确的,MediaBrowserService 仍然是一项服务,可以绑定并检索 IBinder。只是想知道在您的实现中,您是否将其视为普通服务,或者您仍然能够利用MediaBrowser 提供的功能?

以上是关于带有 ExoPlayer 和 PlayerControlView 的 MediaBrowserService - 如何从 UI (PlayerControlView) 访问播放器实例?的主要内容,如果未能解决你的问题,请参考以下文章

带有ima扩展的android exoplayer无法在react native中自动显示广告的倒计时

从 viewpager 2 停止并释放 exoplayer

如何在 android 中更改 ExoPlayer 字幕位置?

ExoPlayer:某些受 PlayReady 保护的资产出现“内部运行时错误”

ExoPlayer 无法播放 Adob​​e 实时流编码器流式传输的音频/视频 (RTMP)

Android 依赖 'com.google.android.exoplayer:exoplayer' 有不同版本的编译(r2.1.0)和运行时(2.9.1)类路径