vendredi 29 mai 2015

VideoView loses buffered portions when switched activity

I wish to keep buffered portions of video which is played in a VideoView. I found out that the VideoView's SurfaceDestroyed has release(true); call, caused releasing of mMediaPlayer instance according to this question VideoView onResume loses buffered portion of the video

I don't want to hack into the internal API so I decides to rewrite SurfaceHolder.Callback and replace it to the mSHCallback field instead to avoid calling release(true); using reflection.

But it seems very strange when I call VideoView.start() in SurfaceCreated override, I've got "Can't play this video' message multiple times but the audio is continue playing through the end even I switch to other activity.

Here is my custom callback

package com.tirkx.aos;

import android.util.Log;
import android.widget.MediaController;
import android.media.MediaPlayer;
import android.view.SurfaceHolder;
import android.widget.VideoView;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CustomVidViewCallbackLoader implements SurfaceHolder.Callback
{
    private final String TAG = "CustomCallbackLoader";

    private Map<String, Field> PrivateFields;
    private Map<String, Method> PrivateMethods;

    private VideoView VideoView;

    boolean VideoOpened;

    public CustomVidViewCallbackLoader(VideoView videoView)
    {
        VideoView = videoView;

        PrivateFields = new HashMap<String, Field>();
        PrivateMethods = new HashMap<String, Method>();
        Class VideoViewClass = videoView.getClass();

        //Reflection things
        try
        {
            PrivateFields.put("mSHCallback", VideoViewClass.getDeclaredField("mSHCallback"));
            PrivateFields.put("mSurfaceHolder", VideoViewClass.getDeclaredField("mSurfaceHolder"));
            PrivateFields.put("mMediaController", VideoViewClass.getDeclaredField("mMediaController"));
            PrivateFields.put("mMediaPlayer", VideoViewClass.getDeclaredField("mMediaPlayer"));
            PrivateFields.put("mSurfaceWidth", VideoViewClass.getDeclaredField("mSurfaceWidth"));
            PrivateFields.put("mSurfaceHeight", VideoViewClass.getDeclaredField("mSurfaceHeight"));
            PrivateFields.put("mTargetState", VideoViewClass.getDeclaredField("mTargetState"));
            PrivateFields.put("mVideoWidth", VideoViewClass.getDeclaredField("mVideoWidth"));
            PrivateFields.put("mVideoHeight", VideoViewClass.getDeclaredField("mVideoHeight"));
            PrivateFields.put("mSeekWhenPrepared", VideoViewClass.getDeclaredField("mSeekWhenPrepared"));

            PrivateMethods.put("openVideo", VideoViewClass.getDeclaredMethod("openVideo"));

            for(Map.Entry<String, Method> m : PrivateMethods.entrySet())
                m.getValue().setAccessible(true);
            for(Map.Entry<String, Field> f : PrivateFields.entrySet())
                f.getValue().setAccessible(true);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }

        try
        {
            SurfaceHolder.Callback SHCallback = (SurfaceHolder.Callback) PrivateFields
                    .get("mSHCallback").get(videoView);
            if (SHCallback != null)
            {
                videoView.getHolder().removeCallback(SHCallback);
                PrivateFields.get("mSHCallback").set(videoView, this);
                videoView.getHolder().addCallback((SurfaceHolder.Callback) PrivateFields
                        .get("mSHCallback").get(videoView));
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        try
        {
            PrivateFields.get("mSurfaceHolder").set(VideoView, holder);
            if(!VideoOpened)
            {
                PrivateMethods.get("openVideo").invoke(VideoView);
                VideoOpened = true;
            }
            else
            {
                VideoView.start();
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        try
        {
            //PrivateFields.get("mSurfaceHolder").set(VideoView, holder);
            int mVideoWidth = (int) PrivateFields.get("mVideoWidth").get(VideoView);
            int mVideoHeight = (int) PrivateFields.get("mVideoHeight").get(VideoView);
            int mTargetState = (int) PrivateFields.get("mTargetState").get(VideoView);
            int mSeekWhenPrepared = (int) PrivateFields.get("mSeekWhenPrepared").get(VideoView);
            MediaPlayer mMediaPlayer = (MediaPlayer) PrivateFields.get("mMediaPlayer").get(VideoView);

            PrivateFields.get("mSurfaceWidth").set(VideoView, width);
            PrivateFields.get("mSurfaceHeight").set(VideoView, height);
            boolean isValidState = (mTargetState == 3);
            boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height);
            if(mMediaPlayer != null && isValidState && hasValidSize)
            {
                if(mSeekWhenPrepared != 0)
                    VideoView.seekTo(mSeekWhenPrepared);

                VideoView.start();
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        try
        {
            PrivateFields.get("mSurfaceHolder").set(VideoView, null);
            MediaController mediaController = (MediaController) PrivateFields.get("mMediaController")
                                                                             .get(VideoView);
            if(mediaController != null)
                mediaController.hide();

            VideoView.pause();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    public void setMediaPlayerOnBufferedChanged(MediaPlayer mediaPlayer)
    {
        if (mediaPlayer != null)
        {
            mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener()
            {
                @Override
                public void onBufferingUpdate(MediaPlayer mp, int percent)
                {
                    Log.i(TAG, "-->" + percent);
                }
            });
        }
    }
}





Aucun commentaire:

Enregistrer un commentaire