【问题标题】:Black screen when returning to video playback activity in Android在Android中返回视频播放活动时黑屏
【发布时间】:2011-06-14 18:01:58
【问题描述】:

我目前正在开发 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(holder) 因为它将当前视图/显示绑定到媒体播放器。当您离开活动时,视图(“持有者”)被销毁。返回活动并重新创建视图后,再次执行 mMediaPlayer.setDisplay(holder) 似乎不会重新附加新创建的视图。显示的是黑屏而不是视频。

有没有人有这个问题的解决方法或解决方案。如有任何帮助或建议,我将不胜感激。

【问题讨论】:

  • 您是否重写了任何生命周期方法,例如 onStop() onResume()?我会从那里开始......
  • 我已经覆盖了它们,你建议我在覆盖的方法中包含什么代码?
  • 听起来你做得对。您在媒体播放器上使用 reset() 吗?除了确保在正确的位置调用正确的 start()、stop()、正确重启等,作为最后的手段,我会附加 android 源代码并在调试器中单步执行。 logcat 中的任何内容?
  • 好主意,我相信调用 reset() 可以解决问题,但是,我正在播放流,不希望在恢复播放之前重新缓冲视频文件的开销。我检查了其他应用程序,例如 vPlayer,它们在后台流式传输视频,而在恢复活动时似乎没有调用 reset()。
  • 这个问题似乎已经用较新版本的 Android(4.x +) 解决了。我在 GB(2.3.6) 和 JB、KK 上运行相同的代码,结果发现 JB 和 KK 可以正常播放视频,而 GB 仍然卡在只有音频输出的黑屏上。

标签: android video screen media-player playback


【解决方案1】:

经过大量的谷歌搜索和对state diagramMediaPlayer 的头疼后,我终于设法避免了这种所谓的黑屏。我已经在 OP 的 cmets 部分发布了这个问题似乎可以用最新的 Android 版本(大于 4.x)解决,所以我选择在 Gingerbread 设备上也有类似的行为。

不用说,SurfaceHolder 回调方法在有界MediaPlayer-SurfaceView 实现的生命周期中起着非常关键的作用。那些非常回调的方法对我来说很方便,可以摆脱这种情况。

第一:

inside 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 将被销毁。我想要实现的是从上下文切换时正在播放的位置继续播放正在进行的视频。 所以,为了做到这一点,

将当前播放位置保存在一个变量中,以便我们以后可以使用它寻找我们的播放到该特定位置。 还有一个。释放该死的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。

【讨论】:

  • 感谢您提供必要的解决方案
  • @Puru Pawar 你好。可以为TextureView提供解决方案吗?
【解决方案2】:

我想我知道问题的原因。似乎媒体播放器仅在您调用 prepare/Async() 时才初始化其表面。如果你在那之后改变表面,你会得到你所拥有的。因此解决方案是通过 android:configChanges="orientation|keyboardHidden" 禁用活动的标准生命周期。这将阻止您的活动进行娱乐。否则,您应该在每次重新创建持有人时要求准备。

【讨论】:

【解决方案3】:

您可以执行以下操作:mMediaPlayer.setDisplay(null)surfaceDestroy 以及当您再次输入视频时setDisplay 使用新的surfaceHolder 的 mediaPlayer。
请记住,始终将此代码放在onStart 中,因为当按下主页或锁屏时,它将强制使用新的持有人触发`sufaceCreate'。将重新创建视图,视频将显示为黑屏

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

希望有帮助!!

【讨论】:

  • 你在 2014 年添加了这个答案,所以我必须通知 setType 方法已被弃用。
【解决方案4】:

我也有同样的问题!

在播放视频时,我按下 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();
        }
    }
}

我有空白的黑色背景并听到音频流。

我注意到,如果我在第一次启动此 Activity 时不创建player.setDisplay(holder);,我将有相同的行为

花了两天时间试图解决这个问题...

【讨论】:

    【解决方案5】:

    编辑:请注意,此解决方案不适用于 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】:

      我只是在我的 Galaxy S3 和 Xoom 平板电脑上播放视频时看到了同样的问题。我在播放器设置中关闭了硬件(HW)加速,它解决了这个问题。我不为 Android 开发,但希望这会引导您找到解决方案。也许您可以切换此设置以解决问题。我使用的设备可能没有硬件加速。

      【讨论】:

        【解决方案7】:

        您是否希望在您返回时不显示该活动? 如果是,您可以简单地使用标志

        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        

        如果您希望您的媒体在后台播放,我建议您使用视频表面,因为它一直在刺激我在后台播放,虽然我不想要。有很多方法,但我希望这对你有用 http://www.brightec.co.uk/blog/custom-android-media-controller

        这个链接实际上是用于自定义媒体控制器,但是当我用它创建我的时,我很烦按主页键,视频将在后台播放。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多