Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExoPlayer crashes, possibly trying to draw on surface which has been destroyed #3

Open
CatalystNZ opened this issue Nov 26, 2018 · 8 comments

Comments

@CatalystNZ
Copy link

CatalystNZ commented Nov 26, 2018

I've been trying to debug one issue, which is that ExoPlayer stops functioning, and you get a black screen.

To confirm that the OpenGL thread is still rendering, I have modified a test version to include a new OpenGL Program, and a new set of Shaders which just renders some basic shapes. When the black screen occurs, that all still renders correctly. I feel that it's safe to say, that the issue is that ExoPlayer is stopping/crashing. In other words, the OpenGL pipeline still functions.

That seems to be confirmed by the following log's I encountered when the Rendering stops with the following error, which appears related to ExoPlayer.

E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19

It might be that the Surface is not being destroyed properly when it becomes invalid. That is mentioned in this persons blog -> https://acadroid.wordpress.com/2017/12/12/queuebuffer-error-queuing-buffer-to-surfacetexture-22/

2018-11-26 14:08:07.676 433-1781/net.solusdesigns.cinemagraphlwp E/BufferQueueProducer: [SurfaceTexture-2-433-6] queueBuffer: BufferQueue has been abandoned
2018-11-26 14:08:07.676 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19
2018-11-26 14:08:07.676 433-1781/net.solusdesigns.cinemagraphlwp E/ACodec: [OMX.Exynos.vp8.dec] BaseState::onOutputBufferDrained returning error -19
2018-11-26 14:08:07.676 433-1781/net.solusdesigns.cinemagraphlwp E/ACodec: signalError(omxError 0x80001001, internalError -19)
2018-11-26 14:08:07.676 433-1780/net.solusdesigns.cinemagraphlwp E/MediaCodec: Codec reported err 0xffffffed, actionCode 0, while in state 6
2018-11-26 14:08:07.679 433-1777/net.solusdesigns.cinemagraphlwp E/ExoPlayerImplInternal: Internal runtime error.
    java.lang.IllegalStateException
        at android.media.MediaCodec.native_dequeueOutputBuffer(Native Method)
        at android.media.MediaCodec.dequeueOutputBuffer(MediaCodec.java:2622)
        at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.drainOutputBuffer(MediaCodecRenderer.java:1024)
        at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:573)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:518)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:301)
        at android.os.Handler.dispatchMessage(Handler.java:101)
        at android.os.Looper.loop(Looper.java:164)
        at android.os.HandlerThread.run(HandlerThread.java:65)
2018-11-26 14:08:07.724 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571576f40
2018-11-26 14:08:07.725 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577100
2018-11-26 14:08:07.727 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577480
2018-11-26 14:08:07.727 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577640
2018-11-26 14:08:07.728 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577800
2018-11-26 14:08:07.728 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x75715779c0
2018-11-26 14:08:07.728 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577b80
2018-11-26 14:08:07.729 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577d40
2018-11-26 14:08:07.729 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7571577f00
2018-11-26 14:08:07.729 433-1781/net.solusdesigns.cinemagraphlwp E/Surface: getSlotFromBufferLocked: unknown buffer: 0x75715780c0
2018-11-26 14:08:07.750 433-1777/net.solusdesigns.cinemagraphlwp E/ExoPlayerImplInternal: Stop failed.
    java.lang.IllegalStateException
        at android.media.MediaCodec.native_stop(Native Method)
        at android.media.MediaCodec.stop(MediaCodec.java:2059)
        at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.releaseCodec(MediaCodecRenderer.java:518)
        at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.releaseCodec(MediaCodecVideoRenderer.java:474)
        at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.onDisabled(MediaCodecRenderer.java:474)
        at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.onDisabled(MediaCodecVideoRenderer.java:362)
        at com.google.android.exoplayer2.BaseRenderer.disable(BaseRenderer.java:153)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.disableRenderer(ExoPlayerImplInternal.java:972)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.resetInternal(ExoPlayerImplInternal.java:768)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.stopInternal(ExoPlayerImplInternal.java:731)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:353)
        at android.os.Handler.dispatchMessage(Handler.java:101)
        at android.os.Looper.loop(Looper.java:164)
        at android.os.HandlerThread.run(HandlerThread.java:65)
@yperess
Copy link
Owner

yperess commented Nov 26, 2018

Thanks for all the details. I'll take a look ASAP.

@CatalystNZ
Copy link
Author

CatalystNZ commented Nov 26, 2018

By changing the design of MovieLiveWallpaperService and MovieWallpaperRenderer, I was able to resolve this, and cater better for changes in the video being displayed.

Currently, SimpleExoPlayer is instantiated in the constructor of MovieLiveWallpaperService and passed to MovieWallpaperRenderer in it's constructor:

inner class GlEngine : WallpaperService.Engine(), VideoListener,
            SharedPreferences.OnSharedPreferenceChangeListener {

        private val exoMediaPlayer = initExoMediaPlayer()
        private val renderer = MovieWallpaperRenderer(exoMediaPlayer, mode,
                this@MovieLiveWallpaperService)

I think it is better to release the player when it's not required. Currently the Service holds on to the player for the life of the wallpaper. This means that Video decoder instances are still held by the instance of the player. In addition, the player is not aware when the underlying Surface is being updated. If the video is rendering and MovieWallpaperRenderer createSurfaceTexture() is called, the ExoPlayer can be writing to a Surface which has been destroyed.

What I did in my variant of this code, is create/destroy the ExoPlayer instance (not the MediaSource, that's fine to stay created) each time the Wallpaper visibility changes.

private var exoMediaPlayer: SimpleExoPlayer? = null

In addition I have created a listener interface which the renderer calls when the surface is available (or not)...

    interface OnSurfaceTextureListener {
        fun onSurfaceTextureAvailabilityChanged(isAvailable: Boolean)
    }

I pass the MovieLiveWallpaperService.GLEngine to the Renderer, and call back when the surface is destroyed/created

        lwpGLEngine.onSurfaceTextureAvailabilityChanged(false)
        surfaceTexture?.release()
        surfaceTexture = SurfaceTexture(textures[0]).apply {
            setDefaultBufferSize(videoWidth, videoHeight)
            setOnFrameAvailableListener(this@LWPRenderer)
        }
        lwpGLEngine.onSurfaceTextureAvailabilityChanged(true)

In the MovieLiveWallpaperService I added the following:

    var surfaceTextureAvailable: Boolean = false;
      override fun onSurfaceTextureAvailabilityChanged(isAvailable: Boolean) {
          surfaceTextureAvailable = isAvailable;
          if (surfaceTextureAvailable) {
              updatePlayerSurface()
          }
      }

      fun updatePlayerSurface() {
          if (surfaceTextureAvailable) {
              exoMediaPlayer?.setVideoSurface(Surface(renderer.getSurfaceTexture()))
              exoMediaPlayer?.playWhenReady = true
          } else {
              exoMediaPlayer?.playWhenReady = false
              exoMediaPlayer?.setVideoSurface(null)
          }
      }

      override fun onVisibilityChanged(visible: Boolean) {
          Timber.d("onVisibilityChanged(%s)", visible)
          if (visible) {
              exoMediaPlayer = initExoMediaPlayer()
              exoMediaPlayer?.addVideoListener(this)
              updatePlayerSurface()
              onResume()

          } else {
              exoMediaPlayer?.playWhenReady = false
              exoMediaPlayer?.release()
              onPause()
          }
      }

I would say as well, that it might be nice to see this branch in it's own GitRepo. I think this code will be really helpful to people. Some feedback would be to make the name more useful, change the dependancies to be using the main (non beta/alpha) Kotlin/Android Studio toolsets... remove the RecyclerView imports...

@CatalystNZ
Copy link
Author

CatalystNZ commented Nov 29, 2018

I have submitted another pull request, which I think might solve the issue of ExoPlayer pausing or just going to a black screen

@CatalystNZ
Copy link
Author

Did you get that second pull request OK? I can't seem to see it now. Let me know if you can't see it, and I will resubmit.

@pinguo-sunjianfei
Copy link

I have the same issue

@pinguo-sunjianfei
Copy link

hope author can solve this issue.

@pinguo-sunjianfei
Copy link

I have solved this issue

@pinguo-sunjianfei
Copy link

Reason: exoplayer setPlayWhenReady(true) when onResume, but the glThread will re-create surface, and the last surface will be release. but exoplayer Rendering still going on.
so it must be setPlayWhenReady(true) after glThread re-cerate surface

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants