2011-06-14 210 views
11

我目前正在開發Android應用程序ServeStream,並遇到了無法修復的問題。我的應用程序將使用android MediaPlayer類來傳輸音樂和視頻。我模仿我的課,在發現之後的例子:在Android中返回視頻播放活動時出現黑屏

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(holder)不會重新附加新創建的視圖。顯示黑屏而不是視頻。

有沒有人有解決此問題的解決方法或解決方案。我將不勝感激任何幫助或建議。

+1

你是否重寫了像onStop()onResume()這樣的生命週期方法?我會從那裏開始... – bgs 2011-06-14 18:53:21

+0

我已經覆蓋了他們,你暗示我包含在重寫的方法中的代碼是什麼? – 2011-06-14 20:17:08

+0

聽起來像你在做正確的事。你在媒體播放器上使用reset()嗎?除了確保你正確地調用start(),stop(),在正確的地方重新啓動等等,作爲最後的手段,我會附加android源代碼並在調試器中逐步完成。任何在logcat中? – bgs 2011-06-15 04:57:50

回答

0

我想我知道問題的原因。看起來,mediaplayer只在您調用prepare/Async()時才初始化它的表面。如果之後你會改變表面,你會得到你所擁有的。所以解決方法是通過android:configChanges =「orientation | keyboardHidden」禁用活動的標準生命週期。這會妨礙你的娛樂活動。否則,您應該每次重新創建持卡人時都要求準備。

+0

我很欣賞答案,但我不確定這是否是問題。我已經有configChanges =「orientation | keyboardHidden」並手動處理在我的代碼中更改的配置。請參閱:https://servestream.svn.sourceforge.net/svnroot/servestream/trunk/src/net/sourceforge/servestream/StreamMediaActivity.java但是,返回活動時屏幕仍爲黑色。 – 2011-07-12 05:07:29

0

我有同樣的問題!

在視頻播放過程中,按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

我看到了同樣的問題,簡單地播放視頻在我的Galaxy S3和對Xoom的平板電腦。我在播放器設置中關閉了硬件(HW)加速並解決了問題。我不爲Android開發,但希望這會導致您找到解決方案。也許您可以切換此設置以獲得解決方法。我正在使用的設備可能沒有HW加速。

0

編輯:請注意此解決方案不能在4.x設備上工作 - 視頻根本不會出現,只有空白屏幕,我不能使它在那裏工作。

這裏有一個狀態機,執行以下操作:
1次短几秒鐘的視頻/res/raw/anim_mp4.mp4(程序中的onCreate()方法來發起)的
2.如果用戶按下home鍵然後返回到程序時會尋求視頻開始位置和immediatelly暫停

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佈局(程序中的onResume()方法來發起):

<?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> 

5

很多周圍的Googling和頭刮超過MediaPlayerstate diagram後,我終於設法避免這種所謂的黑屏。我已經在OP的評論部分發布了這個問題,似乎用最新的Android版本(大於4.x)解決了這個問題,所以我選擇在Gingerbread設備上也有類似的行爲。

不用說,SurfaceHolder回調方法在有界的實現的生命週期中起着非常關鍵的作用。而這些非常方便的回調方法對我來說很適合擺脫這種情況。

第一:

的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回調:

不要忘記設置DisplayMediaPlayer和恕我直言,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(); 
    } 
    } 

這就是它的全部。我知道這個問題不在新版本中,所有優秀的但仍有很多GB仍在那裏。

+0

感謝您的解決方案 – Riskhan 2016-03-11 07:59:02

0

你可以這樣做:mMediaPlayer.setDisplay(null)surfaceDestroy和當你再次進入視頻setDisplay mediaPlayer與新surfaceHolder
請記住,總是把這段代碼放在onStart以內,因爲當home或lockscreen時,它會強制`sufaceCreate'被新的持有者觸發。視圖將被重新創建,視頻將顯示爲黑色屏幕

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

希望這個幫助!

+0

您在2014年添加了此答案,因此我必須通知setType方法已被棄用。 – 2015-01-30 12:13:04

0

您想要返回時不顯示該活動嗎? 如果是的話,你可以簡單地使用標誌

i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 

如果你希望你的媒體在後臺播放,我建議你使用視頻表面,因爲它已經讓我感到憤怒在後臺播放,雖然我不想。有辦法,但我希望這將是一個碩果累累的你 http://www.brightec.co.uk/blog/custom-android-media-controller

這個環節實際上是定製的媒體控制器,如你所願,但我用它創建我的,我是不屑與按home鍵,視頻會在後臺播放。