Черный экран при возврате к активности воспроизведения видео в 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
эта ссылка на самом деле предназначена для настройки медиа-контроллеров по вашему желанию, но по мере того, как я создал ее, я был обеспокоен тем, что нажал домашний ключ, видео будет воспроизводиться в фоновом режиме.