Черный экран при возврате к воспроизведению видео в Android - PullRequest
13 голосов
/ 14 июня 2011

Я в настоящее время разрабатываю приложение для Android ServeStream, и я столкнулся с проблемой, которую не могу исправить.Мое приложение будет транслировать музыку и видео с помощью класса Android MediaPlayer.Я смоделировал свой класс после примера, найденного по адресу:

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/media/MediaPlayerDemo_Video.html

Разница между этим примером и моим собственным кодом заключается в том, что мой MediaPlayer работает в службе, которая позволяет ему продолжать воспроизведениена заднем фоне.Проблема с примером кода Android состоит в том, что, если я смотрю видео и покидаю текущее окно / активность (т. Е. Нажимаю кнопку меню и т. Д.) И возвращаюсь к активности воспроизведения, я получаю черный экран, но все равно получаю звук из видеовоспроизводится.

Когда мое воспроизведение начинается изначально, выполняется код, показанный ниже.Этот код по существу создает представление, используемое для воспроизведения, а затем связывает его с медиаплеером:

        setContentView(R.layout.mediaplayer_2);
        mPreview = (SurfaceView) findViewById(R.id.surface);
        holder = mPreview.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
...
        mMediaPlayer.setDisplay(holder);

Важной строкой является mMediaPlayer.setDisplay (holder), поскольку он связывает текущий вид / отображение с медиаплеером.Представление («держатель») уничтожается при выходе из действия.После возврата к действию и воссоздания представления повторное выполнение mMediaPlayer.setDisplay (holder) снова не появляется для повторного присоединения вновь созданного представления.Вместо видео отображается черный экран.

У кого-нибудь есть обходное решение или решение для этой проблемы.Буду признателен за любую помощь или совет.

Ответы [ 7 ]

7 голосов
/ 04 февраля 2014

После долгих поисков и поиска по голове диаграммы состояний из MediaPlayer мне наконец удалось избежать этого так называемого черного экрана. Я уже писал в разделе комментариев OP, что эта проблема, кажется, решена с последними версиями Android (более 4.x), и поэтому я решил, что аналогичное поведение на устройствах Gingerbread.

Само собой разумеется, SurfaceHolder методы обратного вызова играют очень важную роль в жизненном цикле ограниченной реализации MediaPlayer - SurfaceView. И эти методы обратного вызова пригодились мне для выхода из этой ситуации.

First:

inside onCreate (): - Базовые элементы инициализации ..

    mVideoSurface = (SurfaceView) findViewById(R.id.videoSurface);
    videoHolder = mVideoSurface.getHolder();
    videoHolder.addCallback(this);

    // For Android 2.2

    videoHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    // Media Player and Controller

    player = new MediaPlayer();
    controller = new VideoControllerView(this);

Тогда, Обратные вызовы SurfaceView:

не забудьте установить Display на MediaPlayer, и ИМХО, surfaceCreated() - лучшее место для этого.

@Override
 public void surfaceCreated(SurfaceHolder holder) {
    player.setDisplay(holder);
    try {
    player.prepareAsync();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    }
}

А вот и самая важная вещь. Когда пользователь покидает текущее действие, нажав кнопку «Домой» или открыв другое действие, ожидая каких-либо результатов, наш SurfaceView будет уничтожен. Я хотел добиться возобновления воспроизведения текущего видео с той позиции, где оно воспроизводилось, когда контекст переключался. Итак, для этого

Сохраните текущую позицию воспроизведения в переменной, чтобы мы могли использовать ее позже для поиска нашего воспроизведения в этой конкретной позиции. И еще один. Отпустите проклятый экземпляр MediaPlayer. Я пытался избежать выпуска экземпляра MediaPlayer и его воссоздания, но я продолжаю терпеть неудачу снова и снова. Итак ... точка сделана.

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if (player != null) {
        mCurrentVideoPosition = player.getCurrentPosition();
        player.release();
        player = null;
    }

}

Теперь onPrepared () Инициализируйте любой MediaController здесь, если вы заинтересованы. Проверьте, воспроизводилось ли уже видео, и найдите его в этой позиции.

     @Override
 public void onPrepared(MediaPlayer mp) {
    controller.setMediaPlayer(this);
    controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer));
    player.start();

    if (mCurrentVideoPosition != 0) {
        player.seekTo(mCurrentVideoPosition);
        player.start();
    }

}

Наконец, для воспроизведения видео:

 void playVideo(){  

   try {
        if (player == null) 
            player = new MediaPlayer();
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setDataSource("SOME_VIDEO_FILE.3gp");
        player.setOnPreparedListener(this);
        player.setOnErrorListener(new OnErrorListener() {

            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                mp.reset();
                return false;
            }
        });

    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
     }

И это все. Я знаю, что проблема не в новых версиях и все отлично, но ... много ГБ все еще там.

1 голос
/ 20 мая 2014

Вы можете сделать: mMediaPlayer.setDisplay(null) при surfaceDestroy и при повторном вводе видео setDisplay для mediaPlayer с новым surfaceHolder.
Помните, всегда помещайте этот код ниже внутри onStart, потому что когда нажата кнопка homeили экран блокировки, это заставит sufaceCreate сработать с новым держателем.Вид будет воссоздан, видео будет отображаться вместо черного экрана

    holder = mPreview.getHolder();
    holder.addCallback(this);
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Надеюсь, эта помощь !!

0 голосов
/ 11 сентября 2014

Хотите, чтобы активность не отображалась после вашего возвращения? если да, вы можете просто использовать флаг

i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

Если вы хотите, чтобы ваши медиафайлы воспроизводились в фоновом режиме, я предлагаю вам использовать видео поверхность, так как это раздражало меня, играя в фоновом режиме, хотя я этого не хочу. Есть способы, но я надеюсь, что этот будет для вас плодотворным http://www.brightec.co.uk/blog/custom-android-media-controller

эта ссылка на самом деле предназначена для настройки медиа-контроллеров по вашему желанию, но когда я создал свой с ним, мне надоело нажимать клавишу домой, видео будет воспроизводиться в фоновом режиме.

0 голосов
/ 20 августа 2012

РЕДАКТИРОВАТЬ: обратите внимание, что это решение не работает на устройствах 4.x - видео просто никогда не появляется, только пустой экран, и я не мог заставить его работать там.

Вот конечный автомат, который выполняет следующие действия:
1. проигрывает пару секунд видео /res/raw/anim_mp4.mp4 (процедура инициирована в методе onCreate ())
2. если пользователь нажимает кнопку «Домой», а затем возвращается в приложение, он будет искать видео в начальной позиции и немедленно останавливаться (процедура, инициированная в методе onResume ())

package com.test.video;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class MediaPlayerActivity extends Activity implements OnCompletionListener,
    MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener, SurfaceHolder.Callback {

private final int STATE_NOT_INITIALIZED = 0;
private final int STATE_INITIALIZING = 1;
private final int STATE_INITIALIZED = 2;
private final int STATE_SEEKING = 3;
private final int STATE_DELAYING = 4;
private final int STATE_PLAYING = 5;
private final int STATE_FINISHED = 6;
private final int STATE_RESUMED = 7;
private final int STATE_RESUMED_PREPARING = 8;
private final int STATE_RESUMED_PREPARED = 9;
private final int STATE_RESUMED_SEEKING = 10;
private int state = STATE_NOT_INITIALIZED;

private SurfaceView surface;
private MediaPlayer player;
private SurfaceHolder holder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Log.d(Constants.TAG, "onCreate()");
    super.onCreate(savedInstanceState);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.mediaplayer);

    surface = (SurfaceView) findViewById(R.id.idSurface);
    holder = surface.getHolder();
    holder.addCallback(this);
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
protected void onStart() {
    Log.d(Constants.TAG, "onStart()");
    super.onStart();
    state();
}

@Override
protected void onResume() {
    Log.d(Constants.TAG, "onResume()");
    super.onResume();
    if (STATE_FINISHED == state) {
        state = STATE_RESUMED;
        state();
    }
}

@Override
protected void onDestroy() {
    Log.d(Constants.TAG, "onDestroy()");
    super.onDestroy();
    if (player != null) {
        player.release();
        player = null;
    }
}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    Log.d(Constants.TAG, "surfaceChanged()");
}

@Override
public void surfaceCreated(SurfaceHolder arg0) {
    Log.d(Constants.TAG, "surfaceCreated()");
}

@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
    Log.d(Constants.TAG, "surfaceDestroyed()");
}

@Override
public void onPrepared(MediaPlayer mp) {
    Log.d(Constants.TAG, "onPrepared()");
    state();
}

@Override
public void onCompletion(MediaPlayer mp) {
    Log.d(Constants.TAG, "onCompletion()");
    state();
}

@Override
public void onSeekComplete(MediaPlayer mediaplayer) {
    Log.d(Constants.TAG, "onSeekComplete()");
    state();
}

private class ResumeDelayed extends PlayDelayed {
    protected void onPostExecute(Void result) {
        Log.d(Constants.TAG, "ResumeDelayed.onPostExecute()");
        state();
    };
}

private void initPlayer() {
    Log.d(Constants.TAG, "initPlayer()");
    try {
        if (player == null) {
            player = new MediaPlayer();
            player.setScreenOnWhilePlaying(true);
        } else {
            player.stop();
            player.reset();
        }
        String uri = "android.resource://" + getPackageName() + "/" + R.raw.anim_mp4;
        player.setDataSource(this, Uri.parse(uri));
        player.setDisplay(holder);
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setOnPreparedListener(this);
        player.prepareAsync();
        player.setOnCompletionListener(this);
        player.setOnSeekCompleteListener(this);
    } catch (Throwable t) {
        Log.e(Constants.TAG, "Exception in media prep", t);
    }
}

private class PlayDelayed extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... arg0) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Log.e(Constants.TAG, "Can't sleep", e);
        }
        return null;
    }

    protected void onPostExecute(Void result) {
        Log.d(Constants.TAG, "PlayDelayed.onPostExecute()");
        initPlayer();
    };
}

private void state() {
    switch (state) {
        case STATE_NOT_INITIALIZED:
            state = STATE_INITIALIZING;
            initPlayer();
            break;
        case STATE_INITIALIZING:
            state = STATE_INITIALIZED;
            new PlayDelayed().execute();
            break;
        case STATE_INITIALIZED:
            state = STATE_SEEKING;
            player.start();
            player.seekTo(0);
            break;
        case STATE_SEEKING:
            state = STATE_DELAYING;
            player.pause();
            new ResumeDelayed().execute();
            break;
        case STATE_DELAYING:
            state = STATE_PLAYING;
            player.start();
            break;
        case STATE_PLAYING:
            state = STATE_FINISHED;
            break;
        case STATE_RESUMED:
            state = STATE_RESUMED_PREPARING;
            initPlayer();
            break;
        case STATE_RESUMED_PREPARING:
            state = STATE_RESUMED_PREPARED;
            new PlayDelayed().execute();
            break;
        case STATE_RESUMED_PREPARED:
            state = STATE_RESUMED_SEEKING;
            player.start();
            player.seekTo(0);
            break;
        case STATE_RESUMED_SEEKING:
            state = STATE_FINISHED;
            player.pause();
            break;
        default:
            break;
    }
}

макет mediaplayer.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/black" >

<SurfaceView
    android:id="@+id/idSurface"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center" />

AndroidManifest.xml выглядит так:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.video"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="10" />

<application
    android:icon="@drawable/ic_launcher"
    android:theme="@android:style/Theme.Black.NoTitleBar" >
    <activity
        android:name=".MediaPlayerActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@android:style/Theme.Black.NoTitleBar" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

0 голосов
/ 17 июля 2012

Я видел ту же проблему при воспроизведении видео на моем Galaxy S3 и на планшете Xoom.Я отключил аппаратное (HW) ускорение в настройках плеера и это решило проблему.Я не разрабатываю для Android, но, надеюсь, это приведет вас к решению.Может быть, вы можете переключить эту настройку для обходного пути.Устройство, которое я использую, может не иметь ускорения HW.

0 голосов
/ 12 июля 2012

У меня такая же проблема!

Во время воспроизведения видео я нажимаю кнопку HOME, а затем возвращаюсь в приложение. Я получаю:

public void surfaceCreated(SurfaceHolder holder) {
    player.setDisplay(holder);
    playVideo();
}

В playVideo() у меня есть:

private void playVideo() {
    if (extras.getString("video_path").equals("VIDEO_URI")) {
        showToast("Please, set the video URI in HelloAndroidActivity.java in onClick(View v) method");
    } else {
        try {
            if (flag == 0) {
                player.setDataSource(extras.getString("video_path"));
                player.prepareAsync();
                flag = 1;
            }
            else
            {
                player.start();
            }
        } catch (IllegalArgumentException e) {
            showToast("Error while playing video");
            Log.i(TAG, "========== IllegalArgumentException ===========");
            e.printStackTrace();
        } catch (IllegalStateException e) {
            showToast("Error while playing video");
            Log.i(TAG, "========== IllegalStateException ===========");
            e.printStackTrace();
        } catch (IOException e) {
            showToast("Error while playing video. Please, check your network connection.");
            Log.i(TAG, "========== IOException ===========");
            e.printStackTrace();
        }
    }
}

А у меня чистый черный фон и слышу аудиопоток.

Я заметил, что если при первом запуске этого Действия я не выполню player.setDisplay(holder);, у меня будет такое же поведение .

Потратил два дня, пытаясь решить эту проблему ...

0 голосов
/ 11 июля 2011

Мне кажется, я знаю причину проблемы. Кажется, что медиаплеер инициализирует свою поверхность ТОЛЬКО при вызове prepare / Async (). Если после этого вы измените поверхность, вы получите то, что получили. Поэтому решение будет состоять в том, чтобы отключить стандартный жизненный цикл активности через android: configChanges = "direction | keyboardHidden". Это предотвратит вашу деятельность от отдыха. В противном случае вы должны вызывать для подготовки каждый раз, когда вы воссоздаете владельца.

...