[英]Black screen when returning to video playback activity in Android
我目前正在開發 android 應用程序 ServeStream,我遇到了無法解決的問題。 我的應用程序將使用 android MediaPlayer class 的 stream 音樂和視頻。 我按照以下示例中的示例對我的 class 進行了建模:
此示例與我自己的代碼之間的區別在於我的 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) 似乎不會重新附加新創建的視圖。 顯示黑屏而不是視頻。
有沒有人有這個問題的解決方法或解決方案。 我將不勝感激任何幫助或建議。
經過大量的谷歌搜索和對MediaPlayer
的state 圖的摸索,我終於設法避免了這種所謂的黑屏。 我已經在 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
,恕我直言, surfaceCreated()
是最好的選擇。
@Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
try {
player.prepareAsync();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
這是最重要的。 當用戶通過按下 Home 按鈕或打開不同的活動並期望任何結果離開當前活動時,我們的SurfaceView
將被銷毀。 我想要實現的是從上下文切換時正在播放的 position 繼續播放正在進行的視頻。 所以,為了做到這一點,
將當前播放 position 保存在一個變量中,以便我們稍后可以使用它來尋找播放到特定的 position。 還有一個。 釋放該死的MediaPlayer
實例。 我試圖避免釋放MediaPlayer
實例並且它正在重新創建,但我一遍又一遍地失敗。 所以..點了。
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (player != null) {
mCurrentVideoPosition = player.getCurrentPosition();
player.release();
player = null;
}
}
現在, onPrepared()如果您有興趣,請在此處初始化任何MediaController
。 檢查視頻是否已經在播放,然后將視頻搜索到 position。
@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();
}
}
這就是全部。 我知道新版本不存在這個問題,而且一切都很好,但是......仍然有很多 GB。
您可以這樣做: mMediaPlayer.setDisplay(null)
當surfaceDestroy
和當您再次輸入視頻時setDisplay
for mediaPlayer with new surfaceHolder
。
請記住,始終將此代碼放在onStart
下面,因為當按下 home 鍵或鎖屏時,它將強制使用新的持有者觸發 `sufaceCreate'。 將重新創建視圖,視頻將顯示為黑屏
holder = mPreview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
希望這有幫助!!
我有同樣的問題!
在播放視頻時,我按 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);
我會有同樣的行為。
花了兩天時間試圖解決這個問題......
我只是在我的 Galaxy S3 和 Xoom 平板電腦上播放視頻時看到了同樣的問題。 我在播放器設置中關閉了硬件(HW)加速,它解決了這個問題。 我不為 Android 開發,但希望這會引導您找到解決方案。 也許您可以切換此設置以解決問題。 我使用的設備可能沒有硬件加速。
編輯:請注意此解決方案不適用於 4.x 設備 - 視頻根本不會出現,只有空白屏幕,我無法讓它在那里工作。
這是一台 state 機器,它執行以下操作:
1. 播放幾秒鍾的視頻 /res/raw/anim_mp4.mp4 (在 onCreate() 方法中啟動的過程)
2. 如果用戶按下主頁按鈕然后返回應用程序,它將尋找視頻以啟動 position 並立即暫停(在 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>
您是否希望在您返回時不顯示活動? 如果是,您可以簡單地使用標志
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
如果您希望您的媒體在后台播放,我建議您使用視頻表面,因為它一直在刺激我在后台播放,盡管我不想要。 有很多方法,但我希望這對你有用 http://www.brightec.co.uk/blog/custom-android-media-controller
此鏈接實際上是用於根據需要自定義媒體控制器,但是當我用它創建我的時,我很煩按 home 鍵,視頻將在后台播放。
我想我知道問題的原因。 似乎媒體播放器僅在您調用 prepare/Async() 時才初始化其表面。 如果你在那之后改變表面,你會得到你所擁有的。 因此解決方案是通過 android:configChanges="orientation|keyboardHidden" 禁用活動的標准生命周期。 這將阻止您的活動進行娛樂。 否則,您應該在每次重新創建持有人時要求准備。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.