Android случайным образом вызывает mediaPlayer onCompletion при изменении ориентации при потоковой передаче видео rtsp

Ищу уже несколько дней и не могу понять, почему mediaPlayer случайным образом срабатывает onCompletion при изменении ориентации устройства. Похоже, что устройства с большим объемом памяти, такие как более мощные планшеты, не так часто выходят из строя, но иногда тоже.

Мой MediaPlayer работает во фрагменте, и я попробовал метод постоянного MediaPlayer с вызовом setRetainInstance (true).

Как подсказка, некоторые устройства иногда сообщают в LogCat, что слишком много работы выполняется в основном потоке, когда это происходит.

Вот мой код фрагмента:

final public class MediaPlayerFragment extends Fragment implements OnCompletionListener, OnPreparedListener, OnBufferingUpdateListener, OnInfoListener, OnErrorListener, SurfaceHolder.Callback {

    /**
     * 
     */
    public final static String  TAG                 = "MediaPlayerFragment";

    private MediaPlayer         mp                  = null;
    private SurfaceView         surfaceView         = null;
    private String              path;

    // create an handler
    private final Handler       myHandler           = new Handler();

    private static final double ASPECT_RATIO        = 4.0 / 3.0;

    /**
     * Update the ui, so that background tasks can call this update on ui thread
     */
    final Runnable              updateUIRunnable    = new Runnable() {
                                                        @Override
                                                        public void run() {
                                                            MediaPlayerFragment.this.updateSurfaceSizeToLayout();
                                                        }
                                                    };

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        Log.v(MediaPlayerFragment.TAG, "INFO: onCreate");
        super.onCreate(savedInstanceState);

        // Control whether a fragment instance is retained across Activity
        // re-creation
        this.setRetainInstance(true);

        // get the extra data from the Intent (Container)
        this.path = this.getArguments().getString("path");
        if (this.path == null)
            throw new RuntimeException("No path where passed to the mediaplayerFragment");
    }

    /**
     * onCreateView
     * 
     * Setup the view of the media player with a surface to show the mediaPlayer
     */
    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
        Log.v(MediaPlayerFragment.TAG, "INFO: onCreateView");

        this.surfaceView = new SurfaceView(this.getActivity());

        // Push surfaceView to maximum available space
        final android.widget.FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        this.surfaceView.setLayoutParams(layoutParams);

        // Placeholder for the area of the video, region to display
        final SurfaceHolder surfaceHolder = this.surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setSizeFromLayout();

        return this.surfaceView;
    }

    @Override
    public void onPause() {
        Log.v(MediaPlayerFragment.TAG, "onPause()");

        if (this.mp != null) {
            this.mp.pause();
            this.mp.setDisplay(null);
        }

        super.onPause();
    }

    /**
     * Prepare the media player for playback. This sets the filepath and start
     * the buffering for the player
     */
    private void preparePlayer() {

        Log.v(MediaPlayerFragment.TAG, "preparePlayer");

        try {
            this.mp.setLooping(false);
            this.mp.setDataSource(this.path);
            this.mp.prepareAsync();

        }
        catch (final IOException e) {
            Log.e(MediaPlayerFragment.TAG, "ERROR: Caught ioExeption" + e.getMessage(), e);
        }
        catch (final Exception e) {
            // IllegalStateException if it is called in an invalid state
            Log.e(MediaPlayerFragment.TAG, "ERROR: " + e.getMessage(), e);
        }
    }

    @Override
    public void onCompletion(final MediaPlayer arg0) {
        Log.d(MediaPlayerFragment.TAG, "onCompletion called");

        this.releaseMediaPlayer();
    }

    @Override
    public void onPrepared(final MediaPlayer mediaplayer) {
        Log.d(MediaPlayerFragment.TAG, "onPrepared called");

        if (!this.mp.isPlaying()) {
            this.mp.start();
        }
    }

    @Override
    public boolean onError(final MediaPlayer arg0, final int arg1, final int arg2) {
        Log.v(MediaPlayerFragment.TAG, "onError MediaPlayer");

        return false;
    }

    /**
     * Release the media player object and set its variable to null.
     */
    private void releaseMediaPlayer() {
        if (this.mp != null) {
            this.mp.reset();
            this.mp.release();
            this.mp = null;
        }
    }

    /**
     * display is turned
     */
    @Override
    public void surfaceCreated(final SurfaceHolder holder) {
        if (this.mp == null) {
            // get a MediaPlayer to display the video
            // store the MediaPlayer in the Application, so it does not get
            this.mp = new MediaPlayer();
            this.mp.setOnCompletionListener(this);
            this.mp.setOnPreparedListener(this); 
            this.mp.setOnBufferingUpdateListener(this);
            this.mp.setOnInfoListener(this);
            this.mp.setOnErrorListener(this); 
            this.mp.setScreenOnWhilePlaying(true);

            this.mp.setDisplay(holder);

            this.preparePlayer();
        } else {
            // re-establish connection to given surfaceHolder
            this.mp.setDisplay(holder);
        }
    }

    @Override
    public void surfaceDestroyed(final SurfaceHolder surfaceHolder1) {
        Log.v(MediaPlayerFragment.TAG, "surfaceDestroyed called");

        // disengage old surface holder
        if (this.mp != null) {
            this.mp.setDisplay(null);
        }
    }

    /**
     * This is called immediately after any structural changes (format or size)
     * have been made to the surface. You should at this point update the
     * imagery in the surface. This method is always called at least once, after
     * surfaceCreated(SurfaceHolder).
     * 
     * @param holder
     *            The SurfaceHolder whose surface has changed.
     * @param format
     *            The new PixelFormat of the surface.
     * @param width
     *            The new width of the surface.
     * @param height
     *            The new height of the surface.
     */
    @Override
    public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
        Log.v(MediaPlayerFragment.TAG, "surfaceChanged called");

        // disengage old surface holder
        if (this.mp != null) {
            this.myHandler.post(this.updateUIRunnable);
            this.mp.setDisplay(holder);
        }
    }

    /**
     * The looks at the dimensions of the surfaceView that holds the MediaPlayer
     * (Video) and update the size of it to fit that dimension. This is
     * necessary because of orientation changes that alters the possible height
     * and width of the surface on that the video is drawn.
     */
    @SuppressWarnings("boxing")
    public void updateSurfaceSizeToLayout() {
        // Surface.ROTATION_0 (no rotation), Surface.ROTATION_90,
        // Surface.ROTATION_180, or Surface.ROTATION_270
        final int orientation = this.getResources().getConfiguration().orientation;

        Log.d(MediaPlayerFragment.TAG, "Orientation :" + orientation);

        // Get the SurfaceView layout parameters
        final android.view.ViewGroup.LayoutParams lp = this.surfaceView.getLayoutParams();

        final FrameLayout frameLayout = (FrameLayout) this.surfaceView.getParent();
        final int width = frameLayout.getWidth();
        final int height = frameLayout.getHeight();

        if (orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT) {
            // Display in portrait
            lp.width = LayoutParams.MATCH_PARENT;
            lp.height = (int) (width / MediaPlayerFragment.ASPECT_RATIO);
            Log.d(MediaPlayerFragment.TAG, String.format("New surfaceView size: width:max ,height:%d", lp.height));
        } else {
            lp.width = (int) (height * MediaPlayerFragment.ASPECT_RATIO);
            lp.height = LayoutParams.MATCH_PARENT;
            Log.d(MediaPlayerFragment.TAG, String.format("New surfaceView size: width:%d , height: max", lp.width));
        }

        // Commit the layout parameters
        this.surfaceView.setLayoutParams(lp);
        this.surfaceView.getHolder().setSizeFromLayout();
    }
}

Может ли кто-нибудь увидеть большую ошибку в том, как я обрабатываю SurfaceView MediaPlayer или что-то еще, чтобы приложение вылетало.

Вот лог от LogCat после его двойного поворота и при втором изменении ориентации он разбился:

09-02 18:59:05.796: W/MediaPlayer(19521): info/warning (702, 0)
09-02 18:59:05.806: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:702, extra:0
09-02 18:59:05.836: W/MediaPlayer(19521): info/warning (701, 0)
09-02 18:59:05.836: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:701, extra:0
09-02 18:59:06.356: V/MediaPlayerFragment(19521): DEBUG: onPause
09-02 18:59:06.436: V/MediaPlayerFragment(19521): surfaceDestroyed called
09-02 18:59:06.466: V/MediaPlayerFragment(19521): onDetach
09-02 18:59:06.546: D/MPActivity(19521): onCreate
09-02 18:59:06.546: V/MediaPlayerFragment(19521): DEBUG: onAttach
09-02 18:59:06.596: D/MPActivity(19521): addVideoFragment
09-02 18:59:06.596: V/MediaPlayerFragment(19521): INFO: onCreateView
09-02 18:59:06.606: V/MediaPlayerFragment(19521): DEBUG: onResume
09-02 18:59:06.696: D/MediaPlayerFragment(19521): surfaceCreated called
09-02 18:59:06.726: V/MediaPlayerFragment(19521): surfaceChanged called
09-02 18:59:06.796: D/MediaPlayerFragment(19521): Orientation :2
09-02 18:59:06.796: D/MediaPlayerFragment(19521): width:810 ,height:-1
09-02 18:59:06.826: V/MediaPlayerFragment(19521): surfaceChanged called
09-02 18:59:06.906: D/MediaPlayerFragment(19521): Orientation :2
09-02 18:59:06.916: D/MediaPlayerFragment(19521): width:810 ,height:-1
09-02 18:59:07.886: W/MediaPlayer(19521): info/warning (702, 0)
09-02 18:59:07.886: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:702, extra:0
09-02 18:59:08.186: W/MediaPlayer(19521): info/warning (701, 0)
09-02 18:59:08.186: D/MediaPlayerFragment(19521): MediaPlayer.OnInfoListener: what:701, extra:0
09-02 18:59:09.036: V/MediaPlayerFragment(19521): DEBUG: onPause
09-02 18:59:09.056: V/MediaPlayerFragment(19521): surfaceDestroyed called
09-02 18:59:09.066: V/MediaPlayerFragment(19521): onDetach
09-02 18:59:09.086: E/MediaPlayer(19521): error (1, -2147483648)
09-02 18:59:09.086: D/MPActivity(19521): onCreate
09-02 18:59:09.086: V/MediaPlayerFragment(19521): DEBUG: onAttach
09-02 18:59:09.116: D/MPActivity(19521): addVideoFragment
09-02 18:59:09.116: V/MediaPlayerFragment(19521): INFO: onCreateView
09-02 18:59:09.126: V/MediaPlayerFragment(19521): DEBUG: onResume
09-02 18:59:09.176: D/MediaPlayerFragment(19521): surfaceCreated called
09-02 18:59:09.196: V/MediaPlayerFragment(19521): surfaceChanged called
09-02 18:59:09.196: E/MediaPlayer(19521): Error (1,-2147483648)
09-02 18:59:09.196: V/MediaPlayerFragment(19521): onError MediaPlayer
09-02 18:59:09.196: D/MediaPlayerFragment(19521): onCompletion called
09-02 18:59:09.366: V/MediaPlayerFragment(19521): surfaceDestroyed called
09-02 18:59:09.376: D/MediaPlayerFragment(19521): Orientation :1
09-02 18:59:09.376: D/MediaPlayerFragment(19521): width:-1 ,height:600
09-02 18:59:09.376: V/MediaPlayerFragment(19521): DEBUG: onPause
09-02 18:59:09.396: D/VideoListActivity(19521): onResume()
09-02 18:59:09.496: V/MediaPlayerFragment(19521): INFO: onDestroy
09-02 18:59:09.496: V/MediaPlayerFragment(19521): onDetach

person Dr Evil    schedule 02.09.2014    source источник
comment
Пожалуйста, опубликуйте свой Logcat   -  person MrEngineer13    schedule 02.09.2014


Ответы (1)


О OnCompletionListener Это типичная ошибка, возникающая на некоторых устройствах Samsung. Единственный способ преодолеть это, сохраняя стандартный проигрыватель, — это проверять длину видеоресурса, и если текущая позиция не близка к концу, вы должны игнорировать события OnCompletion.

О setRetainInstance(true): Когда этот метод вызывается, жизненный цикл фрагмента будет: onCreate (только один раз, независимо от того, сколько раз будет повернуто) onCreateView (каждый раз, когда устройство будет повернуто) — здесь воссоздаются все виды, поэтому ваша поверхность, ваш держатель на данный момент являются недействительными. так далее...

Взгляните на этот POC, который я создал, который позволяет вращение. Как непрерывно воспроизводить звук при изменении ориентации в Android?

Удачи

person Gaspar de Elias    schedule 17.07.2015