FileDocCategorySizeDatePackage
VideoView.javaAPI DocAndroid 5.1 API34371Thu Mar 12 22:22:10 GMT 2015android.widget

VideoView

public class VideoView extends android.view.SurfaceView implements android.widget.MediaController.MediaPlayerControl, SubtitleController.Anchor
Displays a video file. The VideoView class can load images from various sources (such as resources or content providers), takes care of computing its measurement from the video so that it can be used in any layout manager, and provides various display options such as scaling and tinting.

Note: VideoView does not retain its full state when going into the background. In particular, it does not restore the current play state, play position, selected tracks, or any subtitle tracks added via {@link #addSubtitleSource addSubtitleSource()}. Applications should save and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and {@link android.app.Activity#onRestoreInstanceState}.

Also note that the audio session id (from {@link #getAudioSessionId}) may change from its previously returned value when the VideoView is restored.

Fields Summary
private String
TAG
private android.net.Uri
mUri
private Map
mHeaders
private static final int
STATE_ERROR
private static final int
STATE_IDLE
private static final int
STATE_PREPARING
private static final int
STATE_PREPARED
private static final int
STATE_PLAYING
private static final int
STATE_PAUSED
private static final int
STATE_PLAYBACK_COMPLETED
private int
mCurrentState
private int
mTargetState
private android.view.SurfaceHolder
mSurfaceHolder
private android.media.MediaPlayer
mMediaPlayer
private int
mAudioSession
private int
mVideoWidth
private int
mVideoHeight
private int
mSurfaceWidth
private int
mSurfaceHeight
private MediaController
mMediaController
private android.media.MediaPlayer.OnCompletionListener
mOnCompletionListener
private MediaPlayer.OnPreparedListener
mOnPreparedListener
private int
mCurrentBufferPercentage
private android.media.MediaPlayer.OnErrorListener
mOnErrorListener
private android.media.MediaPlayer.OnInfoListener
mOnInfoListener
private int
mSeekWhenPrepared
private boolean
mCanPause
private boolean
mCanSeekBack
private boolean
mCanSeekForward
private android.media.SubtitleTrack.RenderingWidget
mSubtitleWidget
Subtitle rendering widget overlaid on top of the video.
private RenderingWidget.OnChangedListener
mSubtitlesChangedListener
Listener for changes to subtitle data, used to redraw when needed.
private Vector
mPendingSubtitleTracks
MediaPlayer.OnVideoSizeChangedListener
mSizeChangedListener
MediaPlayer.OnPreparedListener
mPreparedListener
private android.media.MediaPlayer.OnCompletionListener
mCompletionListener
private android.media.MediaPlayer.OnInfoListener
mInfoListener
private android.media.MediaPlayer.OnErrorListener
mErrorListener
private MediaPlayer.OnBufferingUpdateListener
mBufferingUpdateListener
SurfaceHolder.Callback
mSHCallback
Constructors Summary
public VideoView(android.content.Context context)


       
        super(context);
        initVideoView();
    
public VideoView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
        initVideoView();
    
public VideoView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public VideoView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);
        initVideoView();
    
Methods Summary
public voidaddSubtitleSource(java.io.InputStream is, android.media.MediaFormat format)
Adds an external subtitle source file (from the provided input stream.) Note that a single external subtitle source may contain multiple or no supported tracks in it. If the source contained at least one track in it, one will receive an {@link MediaPlayer#MEDIA_INFO_METADATA_UPDATE} info message. Otherwise, if reading the source takes excessive time, one will receive a {@link MediaPlayer#MEDIA_INFO_SUBTITLE_TIMED_OUT} message. If the source contained no supported track (including an empty source file or null input stream), one will receive a {@link MediaPlayer#MEDIA_INFO_UNSUPPORTED_SUBTITLE} message. One can find the total number of available tracks using {@link MediaPlayer#getTrackInfo()} to see what additional tracks become available after this method call.

param
is input stream containing the subtitle data. It will be closed by the media framework.
param
format the format of the subtitle track(s). Must contain at least the mime type ({@link MediaFormat#KEY_MIME}) and the language ({@link MediaFormat#KEY_LANGUAGE}) of the file. If the file itself contains the language information, specify "und" for the language.

        if (mMediaPlayer == null) {
            mPendingSubtitleTracks.add(Pair.create(is, format));
        } else {
            try {
                mMediaPlayer.addSubtitleSource(is, format);
            } catch (IllegalStateException e) {
                mInfoListener.onInfo(
                        mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
            }
        }
    
private voidattachMediaController()

        if (mMediaPlayer != null && mMediaController != null) {
            mMediaController.setMediaPlayer(this);
            View anchorView = this.getParent() instanceof View ?
                    (View)this.getParent() : this;
            mMediaController.setAnchorView(anchorView);
            mMediaController.setEnabled(isInPlaybackState());
        }
    
public booleancanPause()

        return mCanPause;
    
public booleancanSeekBackward()

        return mCanSeekBack;
    
public booleancanSeekForward()

        return mCanSeekForward;
    
public voiddraw(android.graphics.Canvas canvas)

        super.draw(canvas);

        if (mSubtitleWidget != null) {
            final int saveCount = canvas.save();
            canvas.translate(getPaddingLeft(), getPaddingTop());
            mSubtitleWidget.draw(canvas);
            canvas.restoreToCount(saveCount);
        }
    
public intgetAudioSessionId()

        if (mAudioSession == 0) {
            MediaPlayer foo = new MediaPlayer();
            mAudioSession = foo.getAudioSessionId();
            foo.release();
        }
        return mAudioSession;
    
public intgetBufferPercentage()

        if (mMediaPlayer != null) {
            return mCurrentBufferPercentage;
        }
        return 0;
    
public intgetCurrentPosition()

        if (isInPlaybackState()) {
            return mMediaPlayer.getCurrentPosition();
        }
        return 0;
    
public intgetDuration()

        if (isInPlaybackState()) {
            return mMediaPlayer.getDuration();
        }

        return -1;
    
public android.os.LoopergetSubtitleLooper()

hide

        return Looper.getMainLooper();
    
private voidinitVideoView()

        mVideoWidth = 0;
        mVideoHeight = 0;
        getHolder().addCallback(mSHCallback);
        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        setFocusable(true);
        setFocusableInTouchMode(true);
        requestFocus();
        mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();
        mCurrentState = STATE_IDLE;
        mTargetState  = STATE_IDLE;
    
private booleanisInPlaybackState()

        return (mMediaPlayer != null &&
                mCurrentState != STATE_ERROR &&
                mCurrentState != STATE_IDLE &&
                mCurrentState != STATE_PREPARING);
    
public booleanisPlaying()

        return isInPlaybackState() && mMediaPlayer.isPlaying();
    
private voidmeasureAndLayoutSubtitleWidget()
Forces a measurement and layout pass for all overlaid views.

see
#setSubtitleWidget(RenderingWidget)

        final int width = getWidth() - getPaddingLeft() - getPaddingRight();
        final int height = getHeight() - getPaddingTop() - getPaddingBottom();

        mSubtitleWidget.setSize(width, height);
    
protected voidonAttachedToWindow()

        super.onAttachedToWindow();

        if (mSubtitleWidget != null) {
            mSubtitleWidget.onAttachedToWindow();
        }
    
protected voidonDetachedFromWindow()

        super.onDetachedFromWindow();

        if (mSubtitleWidget != null) {
            mSubtitleWidget.onDetachedFromWindow();
        }
    
public voidonInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)

        super.onInitializeAccessibilityEvent(event);
        event.setClassName(VideoView.class.getName());
    
public voidonInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo info)

        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(VideoView.class.getName());
    
public booleanonKeyDown(int keyCode, android.view.KeyEvent event)

        boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
                                     keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
                                     keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
                                     keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
                                     keyCode != KeyEvent.KEYCODE_MENU &&
                                     keyCode != KeyEvent.KEYCODE_CALL &&
                                     keyCode != KeyEvent.KEYCODE_ENDCALL;
        if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
            if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
                    keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
                if (mMediaPlayer.isPlaying()) {
                    pause();
                    mMediaController.show();
                } else {
                    start();
                    mMediaController.hide();
                }
                return true;
            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
                if (!mMediaPlayer.isPlaying()) {
                    start();
                    mMediaController.hide();
                }
                return true;
            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
                if (mMediaPlayer.isPlaying()) {
                    pause();
                    mMediaController.show();
                }
                return true;
            } else {
                toggleMediaControlsVisiblity();
            }
        }

        return super.onKeyDown(keyCode, event);
    
protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

        super.onLayout(changed, left, top, right, bottom);

        if (mSubtitleWidget != null) {
            measureAndLayoutSubtitleWidget();
        }
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
        //        + MeasureSpec.toString(heightMeasureSpec) + ")");

        int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
        int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
        if (mVideoWidth > 0 && mVideoHeight > 0) {

            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
                // the size is fixed
                width = widthSpecSize;
                height = heightSpecSize;

                // for compatibility, we adjust size based on aspect ratio
                if ( mVideoWidth * height  < width * mVideoHeight ) {
                    //Log.i("@@@", "image too wide, correcting");
                    width = height * mVideoWidth / mVideoHeight;
                } else if ( mVideoWidth * height  > width * mVideoHeight ) {
                    //Log.i("@@@", "image too tall, correcting");
                    height = width * mVideoHeight / mVideoWidth;
                }
            } else if (widthSpecMode == MeasureSpec.EXACTLY) {
                // only the width is fixed, adjust the height to match aspect ratio if possible
                width = widthSpecSize;
                height = width * mVideoHeight / mVideoWidth;
                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    height = heightSpecSize;
                }
            } else if (heightSpecMode == MeasureSpec.EXACTLY) {
                // only the height is fixed, adjust the width to match aspect ratio if possible
                height = heightSpecSize;
                width = height * mVideoWidth / mVideoHeight;
                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    width = widthSpecSize;
                }
            } else {
                // neither the width nor the height are fixed, try to use actual video size
                width = mVideoWidth;
                height = mVideoHeight;
                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // too tall, decrease both width and height
                    height = heightSpecSize;
                    width = height * mVideoWidth / mVideoHeight;
                }
                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // too wide, decrease both width and height
                    width = widthSpecSize;
                    height = width * mVideoHeight / mVideoWidth;
                }
            }
        } else {
            // no size yet, just adopt the given spec sizes
        }
        setMeasuredDimension(width, height);
    
public booleanonTouchEvent(android.view.MotionEvent ev)

        if (isInPlaybackState() && mMediaController != null) {
            toggleMediaControlsVisiblity();
        }
        return false;
    
public booleanonTrackballEvent(android.view.MotionEvent ev)

        if (isInPlaybackState() && mMediaController != null) {
            toggleMediaControlsVisiblity();
        }
        return false;
    
private voidopenVideo()

        if (mUri == null || mSurfaceHolder == null) {
            // not ready for playback just yet, will try again later
            return;
        }
        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        // we shouldn't clear the target state, because somebody might have
        // called start() previously
        release(false);
        try {
            mMediaPlayer = new MediaPlayer();
            // TODO: create SubtitleController in MediaPlayer, but we need
            // a context for the subtitle renderers
            final Context context = getContext();
            final SubtitleController controller = new SubtitleController(
                    context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
            controller.registerRenderer(new WebVttRenderer(context));
            controller.registerRenderer(new TtmlRenderer(context));
            controller.registerRenderer(new ClosedCaptionRenderer(context));
            mMediaPlayer.setSubtitleAnchor(controller, this);

            if (mAudioSession != 0) {
                mMediaPlayer.setAudioSessionId(mAudioSession);
            } else {
                mAudioSession = mMediaPlayer.getAudioSessionId();
            }
            mMediaPlayer.setOnPreparedListener(mPreparedListener);
            mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
            mMediaPlayer.setOnCompletionListener(mCompletionListener);
            mMediaPlayer.setOnErrorListener(mErrorListener);
            mMediaPlayer.setOnInfoListener(mInfoListener);
            mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
            mCurrentBufferPercentage = 0;
            mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
            mMediaPlayer.setDisplay(mSurfaceHolder);
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.setScreenOnWhilePlaying(true);
            mMediaPlayer.prepareAsync();

            for (Pair<InputStream, MediaFormat> pending: mPendingSubtitleTracks) {
                try {
                    mMediaPlayer.addSubtitleSource(pending.first, pending.second);
                } catch (IllegalStateException e) {
                    mInfoListener.onInfo(
                            mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
                }
            }

            // we don't set the target state here either, but preserve the
            // target state that was there before.
            mCurrentState = STATE_PREPARING;
            attachMediaController();
        } catch (IOException ex) {
            Log.w(TAG, "Unable to open content: " + mUri, ex);
            mCurrentState = STATE_ERROR;
            mTargetState = STATE_ERROR;
            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
            return;
        } catch (IllegalArgumentException ex) {
            Log.w(TAG, "Unable to open content: " + mUri, ex);
            mCurrentState = STATE_ERROR;
            mTargetState = STATE_ERROR;
            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
            return;
        } finally {
            mPendingSubtitleTracks.clear();
        }
    
public voidpause()

        if (isInPlaybackState()) {
            if (mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
                mCurrentState = STATE_PAUSED;
            }
        }
        mTargetState = STATE_PAUSED;
    
private voidrelease(boolean cleartargetstate)


    /*
     * release the media player in any state
     */
        
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
            mMediaPlayer.release();
            mMediaPlayer = null;
            mPendingSubtitleTracks.clear();
            mCurrentState = STATE_IDLE;
            if (cleartargetstate) {
                mTargetState  = STATE_IDLE;
            }
        }
    
public intresolveAdjustedSize(int desiredSize, int measureSpec)

        return getDefaultSize(desiredSize, measureSpec);
    
public voidresume()

        openVideo();
    
public voidseekTo(int msec)

        if (isInPlaybackState()) {
            mMediaPlayer.seekTo(msec);
            mSeekWhenPrepared = 0;
        } else {
            mSeekWhenPrepared = msec;
        }
    
public voidsetMediaController(MediaController controller)

        if (mMediaController != null) {
            mMediaController.hide();
        }
        mMediaController = controller;
        attachMediaController();
    
public voidsetOnCompletionListener(android.media.MediaPlayer.OnCompletionListener l)
Register a callback to be invoked when the end of a media file has been reached during playback.

param
l The callback that will be run

        mOnCompletionListener = l;
    
public voidsetOnErrorListener(android.media.MediaPlayer.OnErrorListener l)
Register a callback to be invoked when an error occurs during playback or setup. If no listener is specified, or if the listener returned false, VideoView will inform the user of any errors.

param
l The callback that will be run

        mOnErrorListener = l;
    
public voidsetOnInfoListener(android.media.MediaPlayer.OnInfoListener l)
Register a callback to be invoked when an informational event occurs during playback or setup.

param
l The callback that will be run

        mOnInfoListener = l;
    
public voidsetOnPreparedListener(MediaPlayer.OnPreparedListener l)
Register a callback to be invoked when the media file is loaded and ready to go.

param
l The callback that will be run


                                 
       
    
        mOnPreparedListener = l;
    
public voidsetSubtitleWidget(android.media.SubtitleTrack.RenderingWidget subtitleWidget)

hide

        if (mSubtitleWidget == subtitleWidget) {
            return;
        }

        final boolean attachedToWindow = isAttachedToWindow();
        if (mSubtitleWidget != null) {
            if (attachedToWindow) {
                mSubtitleWidget.onDetachedFromWindow();
            }

            mSubtitleWidget.setOnChangedListener(null);
        }

        mSubtitleWidget = subtitleWidget;

        if (subtitleWidget != null) {
            if (mSubtitlesChangedListener == null) {
                mSubtitlesChangedListener = new RenderingWidget.OnChangedListener() {
                    @Override
                    public void onChanged(RenderingWidget renderingWidget) {
                        invalidate();
                    }
                };
            }

            setWillNotDraw(false);
            subtitleWidget.setOnChangedListener(mSubtitlesChangedListener);

            if (attachedToWindow) {
                subtitleWidget.onAttachedToWindow();
                requestLayout();
            }
        } else {
            setWillNotDraw(true);
        }

        invalidate();
    
public voidsetVideoPath(java.lang.String path)
Sets video path.

param
path the path of the video.

        setVideoURI(Uri.parse(path));
    
public voidsetVideoURI(android.net.Uri uri)
Sets video URI.

param
uri the URI of the video.

        setVideoURI(uri, null);
    
public voidsetVideoURI(android.net.Uri uri, java.util.Map headers)
Sets video URI using specific headers.

param
uri the URI of the video.
param
headers the headers for the URI request. Note that the cross domain redirection is allowed by default, but that can be changed with key/value pairs through the headers parameter with "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to disallow or allow cross domain redirection.

        mUri = uri;
        mHeaders = headers;
        mSeekWhenPrepared = 0;
        openVideo();
        requestLayout();
        invalidate();
    
public voidstart()

        if (isInPlaybackState()) {
            mMediaPlayer.start();
            mCurrentState = STATE_PLAYING;
        }
        mTargetState = STATE_PLAYING;
    
public voidstopPlayback()

        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
            mCurrentState = STATE_IDLE;
            mTargetState  = STATE_IDLE;
        }
    
public voidsuspend()

        release(false);
    
private voidtoggleMediaControlsVisiblity()

        if (mMediaController.isShowing()) {
            mMediaController.hide();
        } else {
            mMediaController.show();
        }