2

我有一个RecyclerView,每个ViewHolder都有RecyclerView一个MediaPlayer对象。我正在使用 aTextureView来保存MediaPlayer对象,问题是当我向下滚动时它非常滞后。为什么会这样?MediaPlayer我相信当表面被破坏时我正在正确地释放对象。

作为参考,我使用了TextureView从这个 repo 中获取的以下实现:TextureVideoView

TextureVideoView.java

public class TextureVideoView extends TextureView implements TextureView.SurfaceTextureListener {

    // Indicate if logging is on
    public static final boolean LOG_ON = true;

    // Log tag
    private static final String TAG = TextureVideoView.class.getName();

    private MediaPlayer mMediaPlayer;

    private float mVideoHeight;
    private float mVideoWidth;

    private boolean mIsDataSourceSet;
    private boolean mIsViewAvailable;
    private boolean mIsVideoPrepared;
    private boolean mIsPlayCalled;

    private ScaleType mScaleType;
    private State mState;

    public enum ScaleType {
        CENTER_CROP, TOP, BOTTOM
    }

    public enum State {
        UNINITIALIZED, PLAY, STOP, PAUSE, END
    }

    public TextureVideoView(Context context) {
        super(context);
        initView();
    }

    public TextureVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public TextureVideoView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    private void initView() {
        if (!isInEditMode()) {
            initPlayer();
            setScaleType(ScaleType.CENTER_CROP);
            setSurfaceTextureListener(this);
        }
    }

    public void setScaleType(ScaleType scaleType) {
        mScaleType = scaleType;
    }

private void updateTextureViewSize() {
    float viewWidth = getWidth();
    float viewHeight = getHeight();

    float scaleX = 1.0f;
    float scaleY = 1.0f;

    if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
        scaleX = mVideoWidth / viewWidth;
        scaleY = mVideoHeight / viewHeight;

    } else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
        scaleY = viewWidth / mVideoWidth;
        scaleX = viewHeight / mVideoHeight;

    } else if (viewWidth > mVideoWidth) {
        scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);

    } else if (viewHeight > mVideoHeight) {
        scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
    }

    // Calculate pivot points, in our case crop from center
    int pivotPointX;
    int pivotPointY;

    switch (mScaleType) {
        case TOP:
            pivotPointX = 0;
            pivotPointY = 0;
            break;
        case BOTTOM:
            pivotPointX = (int) (viewWidth);
            pivotPointY = (int) (viewHeight);
            break;
        case CENTER_CROP:
            pivotPointX = (int) (viewWidth / 2);
            pivotPointY = (int) (viewHeight / 2);
            break;
        default:
            pivotPointX = (int) (viewWidth / 2);
            pivotPointY = (int) (viewHeight / 2);
            break;
    }

    Matrix matrix = new Matrix();
    matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);

    setTransform(matrix);
}

private void initPlayer() {
    if (mMediaPlayer == null) {
        mMediaPlayer = new MediaPlayer();
    } else {
        mMediaPlayer.reset();
    }
    mIsVideoPrepared = false;
    mIsPlayCalled = false;
    mState = State.UNINITIALIZED;
}

/**
 * @see android.media.MediaPlayer#setDataSource(String)
 */
public void setDataSource(final String path) {
    initPlayer();
    ((Activity) getContext()).runOnUiThread(new Runnable() {
        @Override
        public void run() {
            try {
                mMediaPlayer.setDataSource(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    mIsDataSourceSet = true;
    prepare();
}

/**
 * @see android.media.MediaPlayer#setDataSource(android.content.Context, android.net.Uri)
 */
public void setDataSource(Context context, Uri uri) {
    initPlayer();

    try {
        mMediaPlayer.setDataSource(context, uri);
        mIsDataSourceSet = true;
        //prepare();
        mMediaPlayer.setWakeMode(context.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
    } catch (IOException e) {
        Log.d(TAG, e.getMessage());
    }
}

/**
 * @see android.media.MediaPlayer#setDataSource(java.io.FileDescriptor)
 */
public void setDataSource(AssetFileDescriptor afd) {
    initPlayer();

    try {
        long startOffset = afd.getStartOffset();
        long length = afd.getLength();
        mMediaPlayer.setDataSource(afd.getFileDescriptor(), startOffset, length);
        mIsDataSourceSet = true;
        prepare();
    } catch (IOException e) {
        Log.d(TAG, e.getMessage());
    }
}

private void prepare() {

    try {
        // Adjust the size of the MediaPlayer based on the Screen Resolution
        mMediaPlayer.setOnVideoSizeChangedListener(
                new MediaPlayer.OnVideoSizeChangedListener() {
                    @Override
                    public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
                        DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
                        mVideoWidth = width * metrics.density;
                        mVideoHeight = height * metrics.density;
                        updateTextureViewSize();
                    }
                }
        );

        // Add a completion listener for when video ends and pass it to the listener
        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mState = State.END;

                if (mListener != null) {
                    mListener.onVideoEnd();
                }
            }
        });


        // don't forget to call MediaPlayer.prepareAsync() method when you use constructor for
        // creating MediaPlayer
        mMediaPlayer.prepareAsync();

        // Play video when the media source is ready for playback.
        mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                mIsVideoPrepared = true;
                if (mIsPlayCalled && mIsViewAvailable) {
                    log("Player is prepared and play() was called.");
                    play();
                }

                if (mListener != null) {
                    mListener.onVideoPrepared();
                }
            }
        });

    } catch (IllegalArgumentException e) {
        Log.d(TAG, "IllegalArgumentException");
        Log.d(TAG, e.getMessage());
    } catch (SecurityException e) {
        Log.d(TAG, "SecurityException");
        Log.d(TAG, e.getMessage());
    } catch (IllegalStateException e) {
        Log.d(TAG, "IllegalStateException");
        Log.d(TAG, e.toString());
    }
}

/**
 * Play or resume video. Video will be played as soon as view is available and media player is
 * prepared.
 * <p/>
 * If video is stopped or ended and play() method was called, video will start over.
 */
public void play() {
    if (!mIsDataSourceSet) {
        log("play() was called but data source was not set.");
        return;
    }

    mIsPlayCalled = true;

    if (!mIsVideoPrepared) {
        log("play() was called but video is not prepared yet, waiting.");
        return;
    }

    if (!mIsViewAvailable) {
        log("play() was called but view is not available yet, waiting.");
        return;
    }

    if (mState == State.PLAY) {
        log("play() was called but video is already playing.");
        return;
    }

    if (mState == State.PAUSE) {
        log("play() was called but video is paused, resuming.");
        mState = State.PLAY;
        mMediaPlayer.start();
        return;
    }

    if (mState == State.END || mState == State.STOP) {
        log("play() was called but video already ended, starting over.");
        mState = State.PLAY;
        mMediaPlayer.seekTo(0);
        mMediaPlayer.start();
        return;
    }

    mState = State.PLAY;
    mMediaPlayer.start();
}

/**
 * Pause video. If video is already paused, stopped or ended nothing will happen.
 */
public void pause() {
    if (mState == State.PAUSE) {
        log("pause() was called but video already paused.");
        return;
    }

    if (mState == State.STOP) {
        log("pause() was called but video already stopped.");
        return;
    }

    if (mState == State.END) {
        log("pause() was called but video already ended.");
        return;
    }

    mState = State.PAUSE;
    if (mMediaPlayer.isPlaying()) {
        mMediaPlayer.pause();
    }
}

/**
 * Stop video (pause and seek to beginning). If video is already stopped or ended nothing will
 * happen.
 */
public void stop() {
    if (mState == State.UNINITIALIZED) {
        log("stop() was called but video already released.");
    }

    if (mState == State.STOP) {
        log("stop() was called but video already stopped.");
        return;
    }

    if (mState == State.END) {
        log("stop() was called but video already ended.");
        return;
    }

    mState = State.STOP;
    if (mMediaPlayer.isPlaying()) {
        Log.d(TAG, "MediaPlayer is playing");
        mMediaPlayer.pause();
        mMediaPlayer.seekTo(0);
        mMediaPlayer.stop();
    }
}

/**
 * Reset the video (only if the video has been stopped). This will reset the mediaplayer, else
 * nothing happens
 */
public void reset() {
    if (mState == State.PLAY) {
        log("reset() was called but video is currently playing.");
        return;
    }

    if (mState == State.PAUSE) {
        log("reset() was called but video is currently paused.");
        return;
    }

    if (mState == State.STOP) {
        mMediaPlayer.reset();
    }
}

public void release() {
    if (mState == State.PLAY) {
        log("release() was called but video is currently playing.");
        return;
    }

    if (mState == State.PAUSE) {
        log("release() was called but video is currently paused.");
        return;
    }

    if (mState == State.STOP) {
        log("release() was called but video is currently stopped.");
        return;
    }

    mMediaPlayer.release();
    mMediaPlayer = null;
    mState = State.UNINITIALIZED;
}

/**
 * @see android.media.MediaPlayer#setLooping(boolean)
 */
public void setLooping(boolean looping) {
    Log.d(TAG, "Looping");
    mMediaPlayer.setLooping(looping);
}

/**
 * @see android.media.MediaPlayer#seekTo(int)
 */
public void seekTo(int milliseconds) {
    mMediaPlayer.seekTo(milliseconds);
}

/**
 * @see android.media.MediaPlayer#getDuration()
 */
public int getDuration() {
    return mMediaPlayer.getDuration();
}

static void log(String message) {
    if (LOG_ON) {
        Log.d(TAG, message);
    }
}

private MediaPlayerListener mListener;

/**
 * Listener trigger 'onVideoPrepared' and `onVideoEnd` events
 */
public void setListener(MediaPlayerListener listener) {
    mListener = listener;
}

public interface MediaPlayerListener {

    void onVideoPrepared();

    void onVideoEnd();

}

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {

    Surface surface = new Surface(surfaceTexture);
    mMediaPlayer.setSurface(surface);
    mIsViewAvailable = true;
    if (mIsDataSourceSet && mIsPlayCalled && mIsVideoPrepared) {
        play();
    }
}

@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

}

@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    // Deallocate MediaPlayer
    if (mMediaPlayer != null) {
        mMediaPlayer.pause();
        mMediaPlayer.stop();
        mMediaPlayer.reset();
        mMediaPlayer.release();
        mMediaPlayer = null;
    }

    return false;
}

@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {

}

}

当我下载资产时,我会在我的适配器中提供它,如下所示:

final TextureVideoView videoView = holder.videoview;
videoView.setDataSource(path);
videoView.setLooping(true);
videoView.setScaleType(TextureVideoView.ScaleType.CENTER_CROP);
videoView.play();

问题是当我滚动时它真的很迟钝,我该如何解决这个问题?除非视图大部分可见,否则我如何制作RecyclerView像 Vine 一样的暂停。TextureView

寻找这个问题的非常彻底的答案和良好的优化实现。

4

0 回答 0