/* * Copyright (C) 2006 The Android Open Source Project * Copyright (C) 2013 YIXIA.COM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.vov.vitamio.widget; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.PixelFormat; import android.media.AudioManager; import android.net.Uri; import android.os.Build; import android.util.AttributeSet; import android.util.Pair; import android.util.SparseArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup.LayoutParams; import io.vov.vitamio.MediaFormat; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener; import io.vov.vitamio.MediaPlayer.OnCompletionListener; import io.vov.vitamio.MediaPlayer.OnErrorListener; import io.vov.vitamio.MediaPlayer.OnInfoListener; import io.vov.vitamio.MediaPlayer.OnPreparedListener; import io.vov.vitamio.MediaPlayer.OnSeekCompleteListener; import io.vov.vitamio.MediaPlayer.OnTimedTextListener; import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener; import io.vov.vitamio.MediaPlayer.TrackInfo; import io.vov.vitamio.Vitamio; import io.vov.vitamio.utils.Log; import io.vov.vitamio.utils.ScreenResolution; import java.io.IOException; import java.util.List; import java.util.Map; /** * 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. * <p/> * VideoView also provide many wrapper methods for * {@link io.vov.vitamio.MediaPlayer}, such as {@link #getVideoWidth()}, * {@link #setTimedTextShown(boolean)} */ public class VideoView extends SurfaceView implements MediaController.MediaPlayerControl { public static final int VIDEO_LAYOUT_ORIGIN = 0; public static final int VIDEO_LAYOUT_SCALE = 1; public static final int VIDEO_LAYOUT_STRETCH = 2; public static final int VIDEO_LAYOUT_ZOOM = 3; private static final int STATE_ERROR = -1; private static final int STATE_IDLE = 0; private static final int STATE_PREPARING = 1; private static final int STATE_PREPARED = 2; private static final int STATE_PLAYING = 3; private static final int STATE_PAUSED = 4; private static final int STATE_PLAYBACK_COMPLETED = 5; private static final int STATE_SUSPEND = 6; private static final int STATE_RESUME = 7; private static final int STATE_SUSPEND_UNSUPPORTED = 8; OnVideoSizeChangedListener mSizeChangedListener = new OnVideoSizeChangedListener() { public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.d("onVideoSizeChanged: (%dx%d)", width, height); mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); mVideoAspectRatio = mp.getVideoAspectRatio(); if (mVideoWidth != 0 && mVideoHeight != 0) setVideoLayout(mVideoLayout, mAspectRatio); } }; OnPreparedListener mPreparedListener = new OnPreparedListener() { public void onPrepared(MediaPlayer mp) { Log.d("onPrepared"); mCurrentState = STATE_PREPARED; // mTargetState = STATE_PLAYING; // Get the capabilities of the player for this stream //TODO mCanPause if (mOnPreparedListener != null) mOnPreparedListener.onPrepared(mMediaPlayer); if (mMediaController != null) mMediaController.setEnabled(true); mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); mVideoAspectRatio = mp.getVideoAspectRatio(); long seekToPosition = mSeekWhenPrepared; if (seekToPosition != 0) seekTo(seekToPosition); if (mVideoWidth != 0 && mVideoHeight != 0) { setVideoLayout(mVideoLayout, mAspectRatio); if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { if (mTargetState == STATE_PLAYING) { start(); if (mMediaController != null) mMediaController.show(); } else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) { if (mMediaController != null) mMediaController.show(0); } } } else if (mTargetState == STATE_PLAYING) { start(); } } }; SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { mSurfaceWidth = w; mSurfaceHeight = h; boolean isValidState = (mTargetState == STATE_PLAYING); boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h); if (mMediaPlayer != null && isValidState && hasValidSize) { if (mSeekWhenPrepared != 0) seekTo(mSeekWhenPrepared); start(); if (mMediaController != null) { if (mMediaController.isShowing()) mMediaController.hide(); mMediaController.show(); } } } public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND && mTargetState == STATE_RESUME) { mMediaPlayer.setDisplay(mSurfaceHolder); resume(); } else { openVideo(); } } public void surfaceDestroyed(SurfaceHolder holder) { mSurfaceHolder = null; if (mMediaController != null) mMediaController.hide(); release(true); } }; private Uri mUri; private long mDuration; private int mCurrentState = STATE_IDLE; private int mTargetState = STATE_IDLE; private float mAspectRatio = 0; private int mVideoLayout = VIDEO_LAYOUT_SCALE; private SurfaceHolder mSurfaceHolder = null; private MediaPlayer mMediaPlayer = null; private int mVideoWidth; private int mVideoHeight; private float mVideoAspectRatio; private int mVideoChroma = MediaPlayer.VIDEOCHROMA_RGBA; private boolean mHardwareDecoder = false; private int mSurfaceWidth; private int mSurfaceHeight; private MediaController mMediaController; private View mMediaBufferingIndicator; private OnCompletionListener mOnCompletionListener; private OnPreparedListener mOnPreparedListener; private OnErrorListener mOnErrorListener; private OnSeekCompleteListener mOnSeekCompleteListener; private OnTimedTextListener mOnTimedTextListener; private OnInfoListener mOnInfoListener; private OnBufferingUpdateListener mOnBufferingUpdateListener; private int mCurrentBufferPercentage; private long mSeekWhenPrepared; // recording the seek position while preparing private Context mContext; private Map<String, String> mHeaders; private int mBufSize; private OnCompletionListener mCompletionListener = new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { Log.d("onCompletion"); mCurrentState = STATE_PLAYBACK_COMPLETED; mTargetState = STATE_PLAYBACK_COMPLETED; if (mMediaController != null) mMediaController.hide(); if (mOnCompletionListener != null) mOnCompletionListener.onCompletion(mMediaPlayer); } }; private OnErrorListener mErrorListener = new OnErrorListener() { public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { Log.d("Error: %d, %d", framework_err, impl_err); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; if (mMediaController != null) mMediaController.hide(); if (mOnErrorListener != null) { if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) return true; } if (getWindowToken() != null) { int message = framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK ? getResources().getIdentifier("VideoView_error_text_invalid_progressive_playback", "string", mContext.getPackageName()): getResources().getIdentifier("VideoView_error_text_unknown", "string", mContext.getPackageName()); new AlertDialog.Builder(mContext).setTitle(getResources().getIdentifier("VideoView_error_title", "string", mContext.getPackageName())).setMessage(message).setPositiveButton(getResources().getIdentifier("VideoView_error_button", "string", mContext.getPackageName()), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (mOnCompletionListener != null) mOnCompletionListener.onCompletion(mMediaPlayer); } }).setCancelable(false).show(); } return true; } }; private OnBufferingUpdateListener mBufferingUpdateListener = new OnBufferingUpdateListener() { public void onBufferingUpdate(MediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; if (mOnBufferingUpdateListener != null) mOnBufferingUpdateListener.onBufferingUpdate(mp, percent); } }; private OnInfoListener mInfoListener = new OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.d("onInfo: (%d, %d)", what, extra); if (mOnInfoListener != null) { mOnInfoListener.onInfo(mp, what, extra); } else if (mMediaPlayer != null) { if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { mMediaPlayer.pause(); if (mMediaBufferingIndicator != null) mMediaBufferingIndicator.setVisibility(View.VISIBLE); } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { mMediaPlayer.start(); if (mMediaBufferingIndicator != null) mMediaBufferingIndicator.setVisibility(View.GONE); } } return true; } }; private OnSeekCompleteListener mSeekCompleteListener = new OnSeekCompleteListener() { @Override public void onSeekComplete(MediaPlayer mp) { Log.d("onSeekComplete"); if (mOnSeekCompleteListener != null) mOnSeekCompleteListener.onSeekComplete(mp); } }; private OnTimedTextListener mTimedTextListener = new OnTimedTextListener() { @Override public void onTimedTextUpdate(byte[] pixels, int width, int height) { Log.i("onSubtitleUpdate: bitmap subtitle, %dx%d", width, height); if (mOnTimedTextListener != null) mOnTimedTextListener.onTimedTextUpdate(pixels, width, height); } @Override public void onTimedText(String text) { Log.i("onSubtitleUpdate: %s", text); if (mOnTimedTextListener != null) mOnTimedTextListener.onTimedText(text); } }; public VideoView(Context context) { super(context); initVideoView(context); } public VideoView(Context context, AttributeSet attrs) { this(context, attrs, 0); initVideoView(context); } public VideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initVideoView(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(mVideoWidth, widthMeasureSpec); int height = getDefaultSize(mVideoHeight, heightMeasureSpec); setMeasuredDimension(width, height); } /** * Set the display options * * @param layout <ul> * <li>{@link #VIDEO_LAYOUT_ORIGIN} * <li>{@link #VIDEO_LAYOUT_SCALE} * <li>{@link #VIDEO_LAYOUT_STRETCH} * <li>{@link #VIDEO_LAYOUT_ZOOM} * </ul> * @param aspectRatio video aspect ratio, will audo detect if 0. */ public void setVideoLayout(int layout, float aspectRatio) { LayoutParams lp = getLayoutParams(); Pair<Integer, Integer> res = ScreenResolution.getResolution(mContext); int windowWidth = res.first.intValue(), windowHeight = res.second.intValue(); float windowRatio = windowWidth / (float) windowHeight; float videoRatio = aspectRatio <= 0.01f ? mVideoAspectRatio : aspectRatio; mSurfaceHeight = mVideoHeight; mSurfaceWidth = mVideoWidth; if (VIDEO_LAYOUT_ORIGIN == layout && mSurfaceWidth < windowWidth && mSurfaceHeight < windowHeight) { lp.width = (int) (mSurfaceHeight * videoRatio); lp.height = mSurfaceHeight; } else if (layout == VIDEO_LAYOUT_ZOOM) { lp.width = windowRatio > videoRatio ? windowWidth : (int) (videoRatio * windowHeight); lp.height = windowRatio < videoRatio ? windowHeight : (int) (windowWidth / videoRatio); } else { boolean full = layout == VIDEO_LAYOUT_STRETCH; lp.width = (full || windowRatio < videoRatio) ? windowWidth : (int) (videoRatio * windowHeight); lp.height = (full || windowRatio > videoRatio) ? windowHeight : (int) (windowWidth / videoRatio); } setLayoutParams(lp); getHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight); Log.d("VIDEO: %dx%dx%f, Surface: %dx%d, LP: %dx%d, Window: %dx%dx%f", mVideoWidth, mVideoHeight, mVideoAspectRatio, mSurfaceWidth, mSurfaceHeight, lp.width, lp.height, windowWidth, windowHeight, windowRatio); mVideoLayout = layout; mAspectRatio = aspectRatio; } @SuppressWarnings("deprecation") private void initVideoView(Context ctx) { mContext = ctx; mVideoWidth = 0; mVideoHeight = 0; getHolder().setFormat(PixelFormat.RGBA_8888); // PixelFormat.RGB_565 getHolder().addCallback(mSHCallback); // this value only use Hardware decoder before Android 2.3 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && mHardwareDecoder) { getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } setFocusable(true); setFocusableInTouchMode(true); requestFocus(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; if (ctx instanceof Activity) ((Activity) ctx).setVolumeControlStream(AudioManager.STREAM_MUSIC); } public boolean isValid() { return (mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid()); } public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } public void setVideoURI(Uri uri) { setVideoURI(uri, null); } public void setVideoURI(Uri uri, Map<String, String> headers) { mUri = uri; mHeaders = headers; mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate(); } public void stopPlayback() { if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; } } private void openVideo() { if (mUri == null || mSurfaceHolder == null || !Vitamio.isInitialized(mContext)) return; Intent i = new Intent("com.android.music.musicservicecommand"); i.putExtra("command", "pause"); mContext.sendBroadcast(i); release(false); try { mDuration = -1; mCurrentBufferPercentage = 0; mMediaPlayer = new MediaPlayer(mContext, mHardwareDecoder); mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mMediaPlayer.setOnInfoListener(mInfoListener); mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); mMediaPlayer.setOnTimedTextListener(mTimedTextListener); mMediaPlayer.setDataSource(mContext, mUri, mHeaders); mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.setBufferSize(mBufSize); mMediaPlayer.setVideoChroma(mVideoChroma == MediaPlayer.VIDEOCHROMA_RGB565 ? MediaPlayer.VIDEOCHROMA_RGB565 : MediaPlayer.VIDEOCHROMA_RGBA); mMediaPlayer.setScreenOnWhilePlaying(true); mMediaPlayer.prepareAsync(); mCurrentState = STATE_PREPARING; attachMediaController(); } catch (IOException ex) { Log.e("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.e("Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); return; } } public void setMediaController(MediaController controller) { if (mMediaController != null) mMediaController.hide(); mMediaController = controller; attachMediaController(); } public void setMediaBufferingIndicator(View mediaBufferingIndicator) { if (mMediaBufferingIndicator != null) mMediaBufferingIndicator.setVisibility(View.GONE); mMediaBufferingIndicator = mediaBufferingIndicator; } private void attachMediaController() { if (mMediaPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); View anchorView = this.getParent() instanceof View ? (View) this.getParent() : this; mMediaController.setAnchorView(anchorView); mMediaController.setEnabled(isInPlaybackState()); if (mUri != null) { List<String> paths = mUri.getPathSegments(); String name = paths == null || paths.isEmpty() ? "null" : paths.get(paths.size() - 1); mMediaController.setFileName(name); } } } public void setOnPreparedListener(OnPreparedListener l) { mOnPreparedListener = l; } public void setOnCompletionListener(OnCompletionListener l) { mOnCompletionListener = l; } public void setOnErrorListener(OnErrorListener l) { mOnErrorListener = l; } public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) { mOnBufferingUpdateListener = l; } public void setOnSeekCompleteListener(OnSeekCompleteListener l) { mOnSeekCompleteListener = l; } public void setOnTimedTextListener(OnTimedTextListener l) { mOnTimedTextListener = l; } public void setOnInfoListener(OnInfoListener l) { mOnInfoListener = l; } private void release(boolean cleartargetstate) { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; if (cleartargetstate) mTargetState = STATE_IDLE; } } @Override public boolean onTouchEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) toggleMediaControlsVisiblity(); return false; } @Override public boolean onTrackballEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) toggleMediaControlsVisiblity(); return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && 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 || keyCode == KeyEvent.KEYCODE_SPACE) { 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); } private void toggleMediaControlsVisiblity() { if (mMediaController.isShowing()) { mMediaController.hide(); } else { mMediaController.show(); } } public void start() { if (isInPlaybackState()) { mMediaPlayer.start(); mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; } public void pause() { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; } } mTargetState = STATE_PAUSED; } public void suspend() { if (isInPlaybackState()) { release(false); mCurrentState = STATE_SUSPEND_UNSUPPORTED; Log.d("Unable to suspend video. Release MediaPlayer."); } } public void resume() { if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND) { mTargetState = STATE_RESUME; } else if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) { openVideo(); } } public long getDuration() { if (isInPlaybackState()) { if (mDuration > 0) return mDuration; mDuration = mMediaPlayer.getDuration(); return mDuration; } mDuration = -1; return mDuration; } public long getCurrentPosition() { if (isInPlaybackState()) return mMediaPlayer.getCurrentPosition(); return 0; } public void seekTo(long msec) { if (isInPlaybackState()) { mMediaPlayer.seekTo(msec); mSeekWhenPrepared = 0; } else { mSeekWhenPrepared = msec; } } public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying(); } public int getBufferPercentage() { if (mMediaPlayer != null) return mCurrentBufferPercentage; return 0; } public void setVolume(float leftVolume, float rightVolume) { if (mMediaPlayer != null) mMediaPlayer.setVolume(leftVolume, rightVolume); } public int getVideoWidth() { return mVideoWidth; } public int getVideoHeight() { return mVideoHeight; } public float getVideoAspectRatio() { return mVideoAspectRatio; } /** * Must set before {@link #setVideoURI} * @param chroma */ public void setVideoChroma(int chroma) { getHolder().setFormat(chroma == MediaPlayer.VIDEOCHROMA_RGB565 ? PixelFormat.RGB_565 : PixelFormat.RGBA_8888); // PixelFormat.RGB_565 mVideoChroma = chroma; } public void setHardwareDecoder(boolean hardware) { mHardwareDecoder= hardware; } public void setVideoQuality(int quality) { if (mMediaPlayer != null) mMediaPlayer.setVideoQuality(quality); } public void setBufferSize(int bufSize) { mBufSize = bufSize; } public boolean isBuffering() { if (mMediaPlayer != null) return mMediaPlayer.isBuffering(); return false; } public String getMetaEncoding() { if (mMediaPlayer != null) return mMediaPlayer.getMetaEncoding(); return null; } public void setMetaEncoding(String encoding) { if (mMediaPlayer != null) mMediaPlayer.setMetaEncoding(encoding); } public SparseArray<MediaFormat> getAudioTrackMap(String encoding) { if (mMediaPlayer != null) return mMediaPlayer.findTrackFromTrackInfo(TrackInfo.MEDIA_TRACK_TYPE_AUDIO, mMediaPlayer.getTrackInfo(encoding)); return null; } public int getAudioTrack() { if (mMediaPlayer != null) return mMediaPlayer.getAudioTrack(); return -1; } public void setAudioTrack(int audioIndex) { if (mMediaPlayer != null) mMediaPlayer.selectTrack(audioIndex); } public void setTimedTextShown(boolean shown) { if (mMediaPlayer != null) mMediaPlayer.setTimedTextShown(shown); } public void setTimedTextEncoding(String encoding) { if (mMediaPlayer != null) mMediaPlayer.setTimedTextEncoding(encoding); } public int getTimedTextLocation() { if (mMediaPlayer != null) return mMediaPlayer.getTimedTextLocation(); return -1; } public void addTimedTextSource(String subPath) { if (mMediaPlayer != null) mMediaPlayer.addTimedTextSource(subPath); } public String getTimedTextPath() { if (mMediaPlayer != null) return mMediaPlayer.getTimedTextPath(); return null; } public void setSubTrack(int trackId) { if (mMediaPlayer != null) mMediaPlayer.selectTrack(trackId); } public int getTimedTextTrack() { if (mMediaPlayer != null) return mMediaPlayer.getTimedTextTrack(); return -1; } public SparseArray<MediaFormat> getSubTrackMap(String encoding) { if (mMediaPlayer != null) return mMediaPlayer.findTrackFromTrackInfo(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT, mMediaPlayer.getTrackInfo(encoding)); return null; } protected boolean isInPlaybackState() { return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); } }