Search code examples
androidsurfaceviewandroid-mediaplayer

The surface has been released when I try to setDisplay to MediaPlayer


My xml file:

<SurfaceView
    android:id="@+id/surfaceView"
    android:layout_marginTop="50dp"
    android:layout_width="fill_parent"
    android:layout_height="300dp" />

My function to setDisplay:

public void playVideo() {
    MediaPlayer mp = new MediaPlayer();
    SurfaceView sv = (SurfaceView) this.findViewById(R.id.surfaceView);
    try {
        mp.setDataSource("sdcard/test/a.3gp");
        SurfaceHolder sh = sv.getHolder();
        mp.setDisplay(sh);***----the exception occured here***
        mp.prepare();
        mp.start();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

the log as below:

04-24 22:19:33.645: W/System.err(16106): java.lang.IllegalArgumentException: The surface has been released
04-24 22:19:33.645: W/System.err(16106):    at android.media.MediaPlayer._setVideoSurface(Native Method)
04-24 22:19:33.645: W/System.err(16106):    at android.media.MediaPlayer.setDisplay(MediaPlayer.java:698)

I have found some similar questions here, but all of those are not suit for me. Waiting for your answers. Thanks very much.


Solution

  • The Surface can be destroyed. That's why you need to add to the a public void surfaceDestroyed(SurfaceHolder holder) to your SurfaceView's implementation like this:

      @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        synchronized (this) {
            hasActiveHolder = false;
    
            synchronized(this)          {
                  this.notifyAll(); 
            }
        } 
    }
    

    You should also add a function that handles Surface creation:

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
         synchronized (this) {
            hasActiveHolder = true;
            this.notifyAll()
         }
    }
    

    And modify your own function this way:

        mp.setDataSource("sdcard/test/a.3gp");
        SurfaceHolder sh = sv.getHolder();
        synchronized (this) {
           while (!hasActiveHolder) {
                  try {
                      this.wait();
                  } catch (InterruptedException e) {
                    //Print something
                  }
            }
            mp.setDisplay(sh);
            mp.prepare();
        }
    

    You have another option which is the way Google suggests you use SurfaceView: in a separate thread.