The surface has been released when I try to setDisplay to MediaPlayer
Asked Answered
O

7

19

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.

Offer answered 24/4, 2013 at 14:41 Comment(0)
M
14

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.

Mammalogy answered 24/4, 2013 at 15:5 Comment(3)
I have tried what you say, but it doesn't work, and the hold screen turn black. Waiting for your more information. Thank you very much for your answer.Offer
I just noticed that in surfaceDestroyed I put a synchronized within a synchronyzed block of the same object. Remove the second one. It seems like you are stuck in waiting. I suggest that if this doesn't work, you attach a debugger and pause all threads you see in order to find which thread is in wait()Mammalogy
I have tried another way to fix it.I move 'setDisplay' to the Callback when the surface been Created('surfaceCreated').It done. Thank you very much for your answer!Offer
S
11

It's something related to the sequence of executing, as the surface has to be created first before setting display for the MediaPlayer, so you have to override the callback method surfaceCreated to the following:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mp.setDisplay(sh); // now "mp" is defined as a class variable
}

and now there is no need to setDisplay inside your play method:

private MediaPlayer mp; // to use it inside surfaceCreated callback method
public void playVideo() {
    mp = new MediaPlayer();
    SurfaceView sv = (SurfaceView) this.findViewById(R.id.surfaceView);
    try {
        mp.setDataSource("sdcard/test/a.3gp");
        SurfaceHolder sh = sv.getHolder();
        mp.prepare();
        mp.start();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Salvidor answered 21/7, 2015 at 9:9 Comment(0)
M
6

Make use of SurfaceHolder.Callback as below

SurfaceView mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
SurfaceHolder holder = mSurfaceView.getHolder();
final MediaPlayer player = new MediaPlayer();

holder.addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        player.setDisplay(holder);
    }

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

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
});

String UrlPath="android.resource://"+getActivity().getPackageName()+"/"+R.raw.your_file_name_without_extension;
try {
    player.setDataSource(getActivity(),Uri.parse(UrlPath));
    player.prepareAsync();
} catch (IOException e) {
    e.printStackTrace();
}
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
    }
});
Melanimelania answered 24/8, 2017 at 7:14 Comment(0)
F
3

The simplest way is just to call setDisplay in surfaceCreated:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mp.setDisplay(holder)
}

and don't forget to unbind surface:

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    mp.setDisplay(null);
}

Note: media player should be initialized somewhere before, for example in onCreate.

Forney answered 8/3, 2016 at 22:37 Comment(0)
A
1

For those still having issues, try implementing SurfaceHolder.Callback in your activity/fragment/etc and at the onCreate/onCreateView method, call the addCallback(SurfaceHolder.Callback callback) method, using your activity/fragment/etc as the parameter callback.

Abiding answered 16/11, 2016 at 17:29 Comment(0)
Z
0

We can look at VideoView source code:

SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback(){
...
    public void surfaceCreated(SurfaceHolder holder)
    {
        mSurfaceHolder = holder;
        openVideo();
    }
    private void openVideo() {
        ...
        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setDisplay(mSurfaceHolder);
    }
}

So we can learn that we should use mediaplayer.setDisplay() in surfaceCreate method be called.

Zohara answered 8/2, 2018 at 5:19 Comment(0)
T
0

In my case I covered sh.unlockCanvasAndPost with

if(sh.getSurface().isValid()){
    sh.unlockCanvasAndPost(canvas);
}

And it seems like recognise case when surface is released. If we will check isValid() source will see:

Returns true if this object holds a valid surface. @return True if it holds a physical surface, so lockCanvas() will succeed.Otherwise returns false.

Tanney answered 29/8, 2022 at 14:17 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.