androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
https://developer.android.com/media/media3
Apache License 2.0
1.7k stars 406 forks source link

Media3 ExoPlayer stops playback of audio after phone sleeps #22

Open andycass opened 2 years ago

andycass commented 2 years ago

Extending MediaLibraryService and setting the wake mode - C.WAKE_MODE_LOCAL, playback stops after the phone goes to sleep.

Player class;

@SuppressLint("UnsafeOptInUsageError")
class MusicPlayerService @Inject constructor(): MediaLibraryService() {

    private lateinit var player: ExoPlayer
    private lateinit var mediaLibrarySession: MediaLibrarySession
    private val librarySessionCallback = CustomMediaLibrarySessionCallback()

    companion object {
        val TAG: String = MusicPlayerService::class.java.simpleName
        const val PLAY = 0
        const val PLAY_NEXT = 1
        const val ADD_TO_QUEUE = 2
    }

    override fun onCreate() {
        super.onCreate()
        initializeSessionAndPlayer()
    }

    override fun onDestroy() {
        player.release()
        mediaLibrarySession.release()
        super.onDestroy()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession {
        return mediaLibrarySession
    }

    // https://www.youtube.com/watch?v=sTIBDcyCmCg
    private fun initializeSessionAndPlayer() {
        player = Builder(this)
            .setAudioAttributes(AudioAttributes.DEFAULT, true)
            .setHandleAudioBecomingNoisy(true)
            .setWakeMode(C.WAKE_MODE_LOCAL)
            .build()

        mediaLibrarySession =
            MediaLibrarySession.Builder(this, player, librarySessionCallback)
                .setMediaItemFiller(CustomMediaItemFiller())
                .setId("1")
                .build()
    }

    private inner class CustomMediaLibrarySessionCallback :
        MediaLibrarySession.MediaLibrarySessionCallback

    inner class CustomMediaItemFiller : MediaSession.MediaItemFiller {
        override fun fillInLocalConfiguration(
            session: MediaSession,
            controller: MediaSession.ControllerInfo,
            mediaItem: MediaItem
        ): MediaItem {
            // Return the media item that it will be played
            return mediaItem.buildUpon()
                .setMediaId(mediaItem.mediaId)
                .setUri(mediaItem.mediaMetadata.mediaUri)
                .setMediaMetadata(mediaItem.mediaMetadata)
                .build()
        }
    }
}

initialised from a fragment;


@AndroidEntryPoint
class ExoplayerFragment @Inject constructor() :
    BaseAudioFragment(),
    View.OnClickListener {

   override fun onStart() {
        initializeController()
        super.onStart()
    }

    override fun onStop() {
        super.onStop()
        releaseController()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        mDisposable.dispose()
        bottomSheetFragment = null
    }

    override fun onDestroy() {
        super.onDestroy()
        if (!mDisposable.isDisposed) {
            mDisposable.dispose()
        }
    }

    @androidx.annotation.OptIn(UnstableApi::class)
    private fun initializeController() {
        val sessionToken = SessionToken(
            requireContext(),
            ComponentName(requireContext(), MusicPlayerService::class.java)
        )
        controllerFuture =
            MediaController.Builder(
                requireContext(),
                sessionToken
            ).buildAsync()

        controllerFuture.addListener({ setController() }, MoreExecutors.directExecutor())
    }

    private fun releaseController() {
        MediaController.releaseFuture(controllerFuture)
    }
 ...
}

In android manifest included both;

  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
marcbaechinger commented 2 years ago

This works for me when using the demo app.

When you say the device 'is going to sleep' I assume you mean that the device goes into lock screen and then turns off the screen entirely. That's what I tested with the demo app to which I also added the lines that you have on the builder:

.setHandleAudioBecomingNoisy(true)
.setWakeMode(C.WAKE_MODE_LOCAL)

This worked for me at least for a couple of minutes of testing playing the audio media in the background with screen off.

One thing that puzzles me is your choice of WAKE_MODE_LOCAL. Just to check: you are loading the audio media you are playing from the disc, not from the network. Is this correct? I'm asking because WAKE_MODE_LOCAL is documented as follows:

/**
   * A wake mode that will cause the player to hold a {@link android.os.PowerManager.WakeLock}
   * during playback.
   *
   * <p>This is suitable for applications that play media with the screen off and do not load media
   * over wifi.
   */
  public static final int WAKE_MODE_LOCAL = 1;

When this is suitable for apps that do not load media over the network, I would expect that it may be possible that the device thinks that the network can be turned off in this type of wake lock. So just in case you are loading media data to play from the network I would choose WAKE_MODE_NETWORK. If that's not the case, ignore this section. :)

If you still have this problem I would be interested to see the logcat output of the device. Because I would expect that something is logged when the system brings your service down. Easiest would be to create a bug report just after the audio has stopped and upload the bug report here. This contains l the information we need.

andycass commented 2 years ago

Hi Marc, Thanks for the reply

"When you say the device 'is going to sleep' I assume you mean that the device goes into lock screen and then turns off the screen entirely. That's what I tested with the demo app to which I also added the lines that you have on the builder:"

Yes thats correct after starting the mediaItem playing, I lock the screen and the screen turns off seconds later the audio stops playing.

_"One thing that puzzles me is your choice of WAKE_MODE_LOCAL. Just to check: you are loading the audio media you are playing from the disc, not from the network. Is this correct? I'm asking because WAKE_MODELOCAL is documented as follows:"

The test was using media from the local media store (MediaStore.Audio.Media), I have tried both; setWakeMode(C.WAKE_MODE_NETWORK) and
setWakeMode(C.WAKE_MODE_LOCAL)

I have also tried media from the network and have toggled the wake mode in every scenario the problem persists.

Logcat report after playback is initiated and phone is in lock screen with screen off;

1639691295.412 26225-29855 D/CCodecBufferChannel: [c2.sec.mp3.decoder#779] DEBUG: elapsed: n=4 [in=0 pipeline=0 out=0 smoothness=4]
1639691295.412 26225-29855 D/PipelineWatcher: DEBUG: elapsed 0 / 4
1639691296.107 26225-26225 I/ExoPlayerImpl: Release d33cfa4 [AndroidXMedia/1.0.0-alpha] [a71, SM-A715F, samsung, 30] [media3.common, media3.ui, media3.decoder, media3.exoplayer, media3.session, media3.datasource, media3.extractor]
1639691296.114 26225-24916 I/CCodec: state->set(FLUSHING)
1639691296.115 26225-24916 I/CCodec: state->set(FLUSHED)
1639691296.116 26225-24916 D/CCodecBuffers: [c2.sec.mp3.decoder#779:1D-Output.Impl[N]] Client returned a buffer it does not own according to our record: 0
1639691296.116 26225-24916 D/CCodecBuffers: [c2.sec.mp3.decoder#779:1D-Output.Impl[N]] Client returned a buffer it does not own according to our record: 1
1639691296.116 26225-24916 I/CCodec: state->set(RESUMING)
1639691296.116 26225-24916 I/CCodecConfig: query failed after returning 7 values (BAD_INDEX)
1639691296.116 26225-24916 W/Codec2Client: query -- param skipped: index = 1342179345.
1639691296.116 26225-24916 W/Codec2Client: query -- param skipped: index = 2415921170.
1639691296.117 26225-24916 I/CCodec: state->set(RUNNING)
1639691296.122 26225-24916 I/CCodec: state->set(RELEASING)
1639691296.123 26225-24916 D/CCodecBufferChannel: [c2.sec.mp3.decoder#779] MediaCodec discarded an unknown buffer
1639691296.123 26225-24916 D/CCodecBufferChannel: [c2.sec.mp3.decoder#779] MediaCodec discarded an unknown buffer
1639691296.123 26225-24916 D/CCodecBufferChannel: [c2.sec.mp3.decoder#779] MediaCodec discarded an unknown buffer
1639691296.123 26225-24916 D/CCodecBufferChannel: [c2.sec.mp3.decoder#779] MediaCodec discarded an unknown buffer
1639691296.127 26225-25211 I/CCodec: [c2.sec.mp3.decoder] release(1)
1639691296.130 26225-25211 I/CCodec: state->set(RELEASED)
1639691296.130 26225-25211 I/hw-BpHwBinder: onLastStrongRef automatically unlinking death recipients
1639691296.131 26225-24916 I/MediaCodec: Codec shutdown complete
1639691301.132 26225-29859 D/BufferPoolAccessor2.0: bufferpool2 0xb400006ed209bcc8 : 0(0 size) total buffers - 0(0 size) used buffers - 0/5 (recycle/alloc) - 4/3130 (fetch/transfer)
1639691301.133 26225-29859 D/BufferPoolAccessor2.0: evictor expired: 1, evicted: 1

link to bug report; https://drive.google.com/file/d/1iDlWBOXsqf-qYqyyzu4pcEC-DmPm6q8B/view?usp=sharing

google-oss-bot commented 2 years ago

Hey @andycass. We need more information to resolve this issue but there hasn't been an update in 14 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot commented 2 years ago

Hey @andycass. We need more information to resolve this issue but there hasn't been an update in 14 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot commented 2 years ago

Hey @andycass. We need more information to resolve this issue but there hasn't been an update in 14 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!