web-dev-qa-db-fra.com

La surface a été libérée quand j'aie défini de définir sur MediaPlayer.

Mon fichier XML:

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

Ma fonction pour configurer l'affichage:

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

le journal comme ci-dessous:

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)

J'ai trouvé des questions similaires ici, mais toutes ne me conviennent pas. En attente de vos réponses. Merci beaucoup.

17
Cooosuper

La surface peut être détruite. C'est pourquoi vous devez ajouter à la public void surfaceDestroyed(SurfaceHolder holder) de votre implémentation SurfaceView comme ceci:

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

        synchronized(this)          {
              this.notifyAll(); 
        }
    } 
}

Vous devez également ajouter une fonction qui gère la création de surface:

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

Et modifiez votre propre fonction de cette façon:

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

Vous avez une autre option, à savoir la manière dont Google suggère d’utiliser SurfaceView: dans un fil séparé.

13
EyalBellisha

C'est quelque chose lié à la séquence d'exécution, étant donné que la surface doit être créée avant de définir l'affichage pour la variable MediaPlayer. Vous devez donc redéfinir la méthode de rappel surfaceCreated comme suit:

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

et maintenant, il n'est pas nécessaire de définir Display dans votre méthode de lecture:

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();
    }
}
10
Muhammed Refaat

Utilisez SurfaceHolder.Callback comme ci-dessous

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();
    }
});
3
Sathish

La manière la plus simple est d'appeler setDisplay dans surfaceCreated:

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

et n'oubliez pas de délier la surface:

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

Remarque: le lecteur multimédia doit être initialisé quelque part avant, par exemple dans onCreate.

2
Aray

Pour ceux qui ont encore des problèmes, essayez d'implémenter SurfaceHolder.Callback dans votre méthode/fragment/etc et à la méthode onCreate/onCreateView , appelez la méthode addCallback (rappel de SurfaceHolder.Callback) , à l'aide de votre activité/fragment/etc comme paramètre de rappel.

1
Ortiz

Nous pouvons regarder le code source de VideoView:

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

Donc, nous pouvons apprendre que nous devrions utiliser mediaplayer.setDisplay() dans la méthode surfaceCreate être appelée.

0
Chuanxing Zhao