我正在开发Android应用程序ServeStream,我遇到了问题,我无法修复。我的应用程序将使用android MediaPlayer类流式传输音乐和视频。我在以下示例中找到了我的课程:
此示例与我自己的代码之间的区别在于我的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)似乎不会重新附加新创建的视图。显示黑屏而不是视频。
是否有人针对此问题制定了解决方法或解决方案。我将不胜感激任何帮助或建议。
答案 0 :(得分:7)
经过大量的谷歌搜索和MediaPlayer
SurfaceHolder
的{{3}}后,我终于设法避免了这个所谓的黑屏。我已经在OP的评论部分发布了这个问题似乎已经解决了最新的Android版本(大于4.x),因此我选择在Gingerbread设备上也有类似的行为。
毋庸置疑,MediaPlayer
回调方法在有界SurfaceView
- 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);
实施的生命周期中起着至关重要的作用。对于摆脱这种情况,那些非常回调的方法对我来说很方便。
<强>首先强>
在 onCreate(): - 基本初始化内容..
Display
然后, SurfaceView回调:
不要忘记将MediaPlayer
设置为surfaceCreated()
和恕我直言,@Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
try {
player.prepareAsync();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
是最适合这样做的地方。
SurfaceView
这是最重要的事情。当用户通过按Home键离开当前活动或打开一个期望任何结果的不同活动时,我们的MediaPlayer
将被销毁。我想要实现的是从上下文切换时正在播放的位置恢复正在播放的视频。
所以,为了做到这一点,
将当前播放位置保存在变量中,以便我们稍后可以将搜索我们的播放用于该特定位置。
还有一个。释放该死的MediaPlayer
实例。我试图避免释放@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (player != null) {
mCurrentVideoPosition = player.getCurrentPosition();
player.release();
player = null;
}
}
实例并重新创建,但我一直都在失败。所以......点了。
MediaController
现在, onPrepared()
如果您有兴趣,请在此初始化任何 @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}}
这就是全部。我知道问题不在于新版本,而且一切都很好但是......很多GB仍在那里。
答案 1 :(得分:1)
您可以mMediaPlayer.setDisplay(null)
surfaceDestroy
以及再次为新播放setDisplay
的mediaPlayer输入视频时surfaceHolder
。
请记住,始终将此代码放在onStart
内,因为当按下主屏幕或锁定屏幕时,它会强制“sufaceCreate&#39;与新持有人解雇。将重新创建视图,视频将显示黑屏
holder = mPreview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
希望这有帮助!!
答案 2 :(得分:0)
我想我知道问题的原因。只有当你调用prepare / Async()时,媒体播放器才会初始化它的表面。如果你之后改变表面,你将获得你所拥有的。因此解决方案是通过android:configChanges =“orientation | keyboardHidden”禁用活动的标准生命周期。这将阻止您的活动重新创建。否则,每次重新创建持有人时都应该准备好准备。
答案 3 :(得分: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);
,我将具有相同的行为。
花了两天时间试图解决这个问题......
答案 4 :(得分:0)
我在Galaxy S3和Xoom平板电脑上播放视频时遇到了同样的问题。我在播放器设置中关闭了硬件(HW)加速,它解决了问题。我没有为Android开发,但希望这会引导您找到解决方案。也许您可以切换此设置以获得解决方法。我正在使用的设备可能没有硬件加速。
答案 5 :(得分:0)
编辑:请注意此解决方案不适用于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 :(得分:0)
您希望在返回时不显示活动吗? 如果是,你可以简单地使用flag
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
如果你想让你的媒体在后台播放,我建议你使用视频表面,因为虽然我不想要,但它一直让我在后台玩游戏很烦人。有方法,但我希望这个方法对你有益 http://www.brightec.co.uk/blog/custom-android-media-controller
此链接实际上是用于自定义媒体控制器,但是当我用它创建我的时候,我被打扰为按键主页,视频将在后台播放。