Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MediaBrowserService with ExoPlayer and PlayerControlView - How to access to the player instance from the UI (PlayerControlView)?

I am currently working on a part of an app which has video and audio functionality, and started refactor the code base recently. The goal is to integrate MediaSession/ MediaController and MediaBrowserService/ MediaBrowser framework.

We use ExoPlayer and PlayerControlView more specific, the PlayerView for both video and audio components, and it requires the reference to the player instance for the 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) {...

However, under the android developers post and the documentation of MediaBrowserService, the player instance should be contained under the service. In addition, the only way for the client site (MediaBrowser and MediaController) to talk to service it through the connect() method and MediaBrowserConnectionCallback, which makes passing the instance of the player to the PlayerControlView (or the other way around) not possible.

I have tried using the various callbacks such as the MediaSessionCompat.Callback, but neither of the SimpleExoPlayer or the PlayerControlView are Parcelable.

In the traditional service, we uses Binder to access the methods we declared within the service and do something like:

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

However, this seems no possible with the MediaBrowserService/ MediaBrowser framework. I checked the answer to this question, which indicates that using [sendCommand] is a way to call custom methods. But it also requires the parameters to be Parcelable.

To sum up, my question is, is there a way to have the PlayerControlView access to the instance of SimpleExoPlayer or the other way around under the MediaBrowserService framework.

Many thanks ahead for any answer or comments.

like image 360
Mike Hsu Avatar asked Apr 03 '20 18:04

Mike Hsu


1 Answers

In addition, the only way for the client site (MediaBrowser and MediaController) to talk to service it through the connect() method and MediaBrowserConnectionCallback, which makes passing the instance of the player to the PlayerControlView (or the other way around) not possible.

According to my understanding, this is not correct. You can always bind to MediaBrowserService in a traditional way i.e using IBinder to access Service. (Though I am not sure if this is the correct approach or not, otherwise I have to create a static MediaPlayer instance in Service).

I have faced a similar issue with Video Playback using MediaPlayer. I have bound to MediaBrowserService using IBinder and then fetched the MediaPlayer instance. In your service provide a method that returns a reference to MediaPlayer. Something like this:

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;
}

In your Activity/Fragment then bind to MediaBrowserService using IBinder. In my implementation, I have used MediaPlayer, but I think in similar ways it can be used for 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);
}

}

like image 110
A-run Avatar answered Sep 20 '22 15:09

A-run