Черный экран при возврате к активности воспроизведения видео в Android

В настоящее время я разрабатываю приложение 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(держатель), поскольку она связывает текущий вид/отображение с медиаплеером. Взгляд ( "держатель" ) уничтожается, когда вы покидаете действие. После возврата к активности и воссоздания представления, выполнение mMediaPlayer.setDisplay(владельца) снова не появляется, чтобы повторно присоединить вновь созданное представление. Вместо видео отображается черный экран.

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

Ответ 1

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

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

Во-первых:

внутри 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 и IMHO, 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();
    }
     }

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

Ответ 2

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

Ответ 3

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

Во время воспроизведения видео нажмите кнопку 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);, у меня будет то же поведение.

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

Ответ 4

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

Ответ 5

EDIT: обратите внимание, что это решение не работает на устройствах 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>

Ответ 6

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

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

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

Ответ 7

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

i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

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

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