最近需要使用到播放器,自帶的播放器真的爛,網上其實資料根本不多,雖然有幾個開源的大專案,但是都沒有什麼教學,說的非常模糊,找到的部落格也同樣說的很模糊,很難自己二次開發啊。
先說收ijkplayer的使用。引入專案大概需要以下四步
在build.gradle
中新增依賴,根據自己需求新增
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestCompile('com.android.support:support-annotations:26.1.0') {
force = true
}
......
//嗶站的ijkplayer
compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'
//
compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'
// compile 'tv.danmaku.ijk.media:ijkplayer-java:0.6.2'
}
這裏使用了14個組態檔,真的多,分別說說吧
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.util.AttributeSet;
import android.view.View;
import android.widget.MediaController;
import java.util.ArrayList;
public class AndroidMediaController extends MediaController implements IMediaController {
private ActionBar mActionBar;
public AndroidMediaController(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public AndroidMediaController(Context context, boolean useFastForward) {
super(context, useFastForward);
initView(context);
}
public AndroidMediaController(Context context) {
super(context);
initView(context);
}
private void initView(Context context) {
}
public void setSupportActionBar(@Nullable ActionBar actionBar) {
mActionBar = actionBar;
if (isShowing()) {
actionBar.show();
} else {
actionBar.hide();
}
}
@Override
public void show() {
super.show();
if (mActionBar != null)
mActionBar.show();
}
@Override
public void hide() {
super.hide();
if (mActionBar != null)
mActionBar.hide();
for (View view : mShowOnceArray)
view.setVisibility(View.GONE);
mShowOnceArray.clear();
}
//----------
// Extends
//----------
private ArrayList<View> mShowOnceArray = new ArrayList<View>();
public void showOnce(@NonNull View view) {
mShowOnceArray.add(view);
view.setVisibility(View.VISIBLE);
show();
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
public class FileMediaDataSource implements IMediaDataSource {
private RandomAccessFile mFile;
private long mFileSize;
public FileMediaDataSource(File file) throws IOException {
mFile = new RandomAccessFile(file, "r");
mFileSize = mFile.length();
}
@Override
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
if (mFile.getFilePointer() != position)
mFile.seek(position);
if (size == 0)
return 0;
return mFile.read(buffer, 0, size);
}
@Override
public long getSize() throws IOException {
return mFileSize;
}
@Override
public void close() throws IOException {
mFileSize = 0;
mFile.close();
mFile = null;
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.MediaController;
import android.widget.TableLayout;
import android.widget.TextView;
import com.example.k.bilibiliapi.R;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import tv.danmaku.ijk.media.exo.IjkExoMediaPlayer;
import tv.danmaku.ijk.media.player.AndroidMediaPlayer;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
import tv.danmaku.ijk.media.player.IjkTimedText;
import tv.danmaku.ijk.media.player.TextureMediaPlayer;
import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
import tv.danmaku.ijk.media.player.misc.IMediaFormat;
import tv.danmaku.ijk.media.player.misc.ITrackInfo;
import tv.danmaku.ijk.media.player.misc.IjkMediaFormat;
public class IjkVideoView extends FrameLayout implements MediaController.MediaPlayerControl {
private String TAG = "IjkVideoView";
// settable by the client
private Uri mUri;
private Map<String, String> mHeaders;
// all possible internal states
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;
// mCurrentState is a VideoView object's current state.
// mTargetState is the state that a method caller intends to reach.
// For instance, regardless the VideoView object's current state,
// calling pause() intends to bring the object to a target state
// of STATE_PAUSED.
private int mCurrentState = STATE_IDLE;
private int mTargetState = STATE_IDLE;
// All the stuff we need for playing and showing a video
private IRenderView.ISurfaceHolder mSurfaceHolder = null;
private IMediaPlayer mMediaPlayer = null;
// private int mAudioSession;
private int mVideoWidth;
private int mVideoHeight;
private int mSurfaceWidth;
private int mSurfaceHeight;
private int mVideoRotationDegree;
private IMediaController mMediaController;
private IMediaPlayer.OnCompletionListener mOnCompletionListener;
private IMediaPlayer.OnPreparedListener mOnPreparedListener;
private int mCurrentBufferPercentage;
private IMediaPlayer.OnErrorListener mOnErrorListener;
private IMediaPlayer.OnInfoListener mOnInfoListener;
private int mSeekWhenPrepared; // recording the seek position while preparing
private boolean mCanPause = true;
private boolean mCanSeekBack = true;
private boolean mCanSeekForward = true;
/** Subtitle rendering widget overlaid on top of the video. */
// private RenderingWidget mSubtitleWidget;
/**
* Listener for changes to subtitle data, used to redraw when needed.
*/
// private RenderingWidget.OnChangedListener mSubtitlesChangedListener;
private Context mAppContext;
private Settings mSettings;
private IRenderView mRenderView;
private int mVideoSarNum;
private int mVideoSarDen;
private InfoHudViewHolder mHudViewHolder;
private long mPrepareStartTime = 0;
private long mPrepareEndTime = 0;
private long mSeekStartTime = 0;
private long mSeekEndTime = 0;
private TextView subtitleDisplay;
public IjkVideoView(Context context) {
super(context);
initVideoView(context);
}
public IjkVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
initVideoView(context);
}
public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initVideoView(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initVideoView(context);
}
// REMOVED: onMeasure
// REMOVED: onInitializeAccessibilityEvent
// REMOVED: onInitializeAccessibilityNodeInfo
// REMOVED: resolveAdjustedSize
private void initVideoView(Context context) {
mAppContext = context.getApplicationContext();
mSettings = new Settings(mAppContext);
initBackground();
initRenders();
mVideoWidth = 0;
mVideoHeight = 0;
// REMOVED: getHolder().addCallback(mSHCallback);
// REMOVED: getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
// REMOVED: mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
subtitleDisplay = new TextView(context);
subtitleDisplay.setTextSize(24);
subtitleDisplay.setGravity(Gravity.CENTER);
LayoutParams layoutParams_txt = new LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM);
addView(subtitleDisplay, layoutParams_txt);
}
public void setRenderView(IRenderView renderView) {
if (mRenderView != null) {
if (mMediaPlayer != null)
mMediaPlayer.setDisplay(null);
View renderUIView = mRenderView.getView();
mRenderView.removeRenderCallback(mSHCallback);
mRenderView = null;
removeView(renderUIView);
}
if (renderView == null)
return;
mRenderView = renderView;
renderView.setAspectRatio(mCurrentAspectRatio);
if (mVideoWidth > 0 && mVideoHeight > 0)
renderView.setVideoSize(mVideoWidth, mVideoHeight);
if (mVideoSarNum > 0 && mVideoSarDen > 0)
renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
View renderUIView = mRenderView.getView();
LayoutParams lp = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
renderUIView.setLayoutParams(lp);
addView(renderUIView);
mRenderView.addRenderCallback(mSHCallback);
mRenderView.setVideoRotation(mVideoRotationDegree);
}
public void setAspectRatio(int arAspectFitParent){
if (mRenderView != null) {
mRenderView.setAspectRatio(arAspectFitParent);
}
}
public void setRender(int render) {
switch (render) {
case RENDER_NONE:
setRenderView(null);
break;
case RENDER_SURFACE_VIEW:
case RENDER_TEXTURE_VIEW: {
TextureRenderView renderView = new TextureRenderView(getContext());
if (mMediaPlayer != null) {
renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer);
renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());
renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen());
renderView.setAspectRatio(mCurrentAspectRatio);
}
setRenderView(renderView);
break;
}
default:
Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));
break;
}
}
public void setHudView(TableLayout tableLayout) {
mHudViewHolder = new InfoHudViewHolder(getContext(), tableLayout);
}
/**
* Sets video path.
*
* @param path the path of the video.
*/
public void setVideoPath(String path) {
setVideoURI(Uri.parse(path));
}
/**
* Sets video URI.
*
* @param uri the URI of the video.
*/
public void setVideoURI(Uri uri) {
setVideoURI(uri, null);
}
/**
* 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.
*/
private void setVideoURI(Uri uri, Map<String, String> headers) {
mUri = uri;
mHeaders = headers;
mSeekWhenPrepared = 0;
openVideo();
requestLayout();
invalidate();
}
// REMOVED: addSubtitleSource
// REMOVED: mPendingSubtitleTracks
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
if (mHudViewHolder != null)
mHudViewHolder.setMediaPlayer(null);
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
am.abandonAudioFocus(null);
}
}
@TargetApi(Build.VERSION_CODES.M)
private void openVideo() {
if (mUri == null || mSurfaceHolder == null) {
// not ready for playback just yet, will try again later
return;
}
// we shouldn't clear the target state, because somebody might have
// called start() previously
release(false);
AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
try {
mMediaPlayer = createPlayer(mSettings.getPlayer());
// TODO: create SubtitleController in MediaPlayer, but we need
// a context for the subtitle renderers
final Context context = getContext();
// REMOVED: SubtitleController
// REMOVED: mAudioSession
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mMediaPlayer.setOnTimedTextListener(mOnTimedTextListener);
mCurrentBufferPercentage = 0;
String scheme = mUri.getScheme();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
mSettings.getUsingMediaDataSource() &&
(TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {
IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));
mMediaPlayer.setDataSource(dataSource);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
} else {
mMediaPlayer.setDataSource(mUri.toString());
}
bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mPrepareStartTime = System.currentTimeMillis();
mMediaPlayer.prepareAsync();
if (mHudViewHolder != null)
mHudViewHolder.setMediaPlayer(mMediaPlayer);
// REMOVED: mPendingSubtitleTracks
// 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);
} 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);
} finally {
// REMOVED: mPendingSubtitleTracks.clear();
}
}
public void setMediaController(IMediaController controller) {
if (mMediaController != null) {
mMediaController.hide();
}
mMediaController = controller;
attachMediaController();
}
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());
}
}
IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
new IMediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) {
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
mVideoSarNum = mp.getVideoSarNum();
mVideoSarDen = mp.getVideoSarDen();
if (mVideoWidth != 0 && mVideoHeight != 0) {
if (mRenderView != null) {
mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
}
// REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);
requestLayout();
}
}
};
IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {
public void onPrepared(IMediaPlayer mp) {
mPrepareEndTime = System.currentTimeMillis();
if (mHudViewHolder != null) {
mHudViewHolder.updateLoadCost(mPrepareEndTime - mPrepareStartTime);
}
mCurrentState = STATE_PREPARED;
// Get the capabilities of the player for this stream
// REMOVED: Metadata
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mMediaPlayer);
}
if (mMediaController != null) {
mMediaController.setEnabled(true);
}
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
if (seekToPosition != 0) {
seekTo(seekToPosition);
}
if (mVideoWidth != 0 && mVideoHeight != 0) {
//Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
// REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);
if (mRenderView != null) {
mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
// We didn't actually change the size (it was already at the size
// we need), so we won't get a "surface changed" callback, so
// start the video here instead of in the callback.
if (mTargetState == STATE_PLAYING) {
start();
if (mMediaController != null) {
mMediaController.show();
}
} else if (!isPlaying() &&
(seekToPosition != 0 || getCurrentPosition() > 0)) {
if (mMediaController != null) {
// Show the media controls when we're paused into a video and make 'em stick.
mMediaController.show(0);
}
}
}
}
} else {
// We don't know the video size yet, but should start anyway.
// The video size might be reported to us later.
if (mTargetState == STATE_PLAYING) {
start();
}
}
}
};
private IMediaPlayer.OnCompletionListener mCompletionListener =
new IMediaPlayer.OnCompletionListener() {
public void onCompletion(IMediaPlayer mp) {
mCurrentState = STATE_PLAYBACK_COMPLETED;
mTargetState = STATE_PLAYBACK_COMPLETED;
if (mMediaController != null) {
mMediaController.hide();
}
if (mOnCompletionListener != null) {
mOnCompletionListener.onCompletion(mMediaPlayer);
}
}
};
private IMediaPlayer.OnInfoListener mInfoListener =
new IMediaPlayer.OnInfoListener() {
public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) {
if (mOnInfoListener != null) {
mOnInfoListener.onInfo(mp, arg1, arg2);
}
switch (arg1) {
case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:");
break;
case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:");
break;
case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
Log.d(TAG, "MEDIA_INFO_BUFFERING_START:");
break;
case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
Log.d(TAG, "MEDIA_INFO_BUFFERING_END:");
break;
case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2);
break;
case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:");
break;
case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:");
break;
case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:");
break;
case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:");
break;
case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:");
break;
case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
mVideoRotationDegree = arg2;
Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2);
if (mRenderView != null)
mRenderView.setVideoRotation(arg2);
break;
case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:");
break;
}
return true;
}
};
private IMediaPlayer.OnErrorListener mErrorListener =
new IMediaPlayer.OnErrorListener() {
public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) {
Log.d(TAG, "Error: " + framework_err + "," + impl_err);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
if (mMediaController != null) {
mMediaController.hide();
}
/* If an error handler has been supplied, use it and finish. */
if (mOnErrorListener != null) {
if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
return true;
}
}
/* Otherwise, pop up an error dialog so the user knows that
* something bad has happened. Only try and pop up the dialog
* if we're attached to a window. When we're going away and no
* longer have a window, don't bother showing the user an error.
*/
if (getWindowToken() != null) {
Resources r = mAppContext.getResources();
int messageId;
if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
messageId = R.string.VideoView_error_text_invalid_progressive_playback;
} else {
messageId = R.string.VideoView_error_text_unknown;
}
new AlertDialog.Builder(getContext())
.setMessage(messageId)
.setPositiveButton(R.string.VideoView_error_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* If we get here, there is no onError listener, so
* at least inform them that the video is over.
*/
if (mOnCompletionListener != null) {
mOnCompletionListener.onCompletion(mMediaPlayer);
}
}
})
.setCancelable(false)
.show();
}
return true;
}
};
private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
new IMediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(IMediaPlayer mp, int percent) {
mCurrentBufferPercentage = percent;
}
};
private IMediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(IMediaPlayer mp) {
mSeekEndTime = System.currentTimeMillis();
mHudViewHolder.updateSeekCost(mSeekEndTime - mSeekStartTime);
}
};
private IMediaPlayer.OnTimedTextListener mOnTimedTextListener = new IMediaPlayer.OnTimedTextListener() {
@Override
public void onTimedText(IMediaPlayer mp, IjkTimedText text) {
if (text != null) {
subtitleDisplay.setText(text.getText());
}
}
};
/**
* Register a callback to be invoked when the media file
* is loaded and ready to go.
*
* @param l The callback that will be run
*/
public void setOnPreparedListener(IMediaPlayer.OnPreparedListener l) {
mOnPreparedListener = 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
*/
public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) {
mOnCompletionListener = 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
*/
public void setOnErrorListener(IMediaPlayer.OnErrorListener l) {
mOnErrorListener = l;
}
/**
* Register a callback to be invoked when an informational event
* occurs during playback or setup.
*
* @param l The callback that will be run
*/
public void setOnInfoListener(IMediaPlayer.OnInfoListener l) {
mOnInfoListener = l;
}
// REMOVED: mSHCallback
private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) {
if (mp == null)
return;
if (holder == null) {
mp.setDisplay(null);
return;
}
holder.bindToMediaPlayer(mp);
}
IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() {
@Override
public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) {
if (holder.getRenderView() != mRenderView) {
Log.e(TAG, "onSurfaceChanged: unmatched render callback\n");
return;
}
mSurfaceWidth = w;
mSurfaceHeight = h;
boolean isValidState = (mTargetState == STATE_PLAYING);
boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);
if (mMediaPlayer != null && isValidState && hasValidSize) {
if (mSeekWhenPrepared != 0) {
seekTo(mSeekWhenPrepared);
}
start();
}
}
@Override
public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) {
if (holder.getRenderView() != mRenderView) {
Log.e(TAG, "onSurfaceCreated: unmatched render callback\n");
return;
}
mSurfaceHolder = holder;
if (mMediaPlayer != null)
bindSurfaceHolder(mMediaPlayer, holder);
else
openVideo();
}
@Override
public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {
if (holder.getRenderView() != mRenderView) {
Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n");
return;
}
// after we return from this we can't use the surface any more
mSurfaceHolder = null;
// REMOVED: if (mMediaController != null) mMediaController.hide();
// REMOVED: release(true);
releaseWithoutStop();
}
};
public void releaseWithoutStop() {
if (mMediaPlayer != null)
mMediaPlayer.setDisplay(null);
}
/*
* release the media player in any state
*/
public void release(boolean cleartargetstate) {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
// REMOVED: mPendingSubtitleTracks.clear();
mCurrentState = STATE_IDLE;
if (cleartargetstate) {
mTargetState = STATE_IDLE;
}
AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
am.abandonAudioFocus(null);
}
}
@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_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);
}
private void toggleMediaControlsVisiblity() {
if (mMediaController.isShowing()) {
mMediaController.hide();
} else {
mMediaController.show();
}
}
@Override
public void start() {
if (isInPlaybackState()) {
mMediaPlayer.start();
mCurrentState = STATE_PLAYING;
}
mTargetState = STATE_PLAYING;
}
@Override
public void pause() {
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
mCurrentState = STATE_PAUSED;
}
}
mTargetState = STATE_PAUSED;
}
public void suspend() {
release(false);
}
public void resume() {
openVideo();
}
@Override
public int getDuration() {
if (isInPlaybackState()) {
return (int) mMediaPlayer.getDuration();
}
return -1;
}
@Override
public int getCurrentPosition() {
if (isInPlaybackState()) {
return (int) mMediaPlayer.getCurrentPosition();
}
return 0;
}
@Override
public void seekTo(int msec) {
if (isInPlaybackState()) {
mSeekStartTime = System.currentTimeMillis();
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
}
}
@Override
public boolean isPlaying() {
return isInPlaybackState() && mMediaPlayer.isPlaying();
}
@Override
public int getBufferPercentage() {
if (mMediaPlayer != null) {
return mCurrentBufferPercentage;
}
return 0;
}
private boolean isInPlaybackState() {
return (mMediaPlayer != null &&
mCurrentState != STATE_ERROR &&
mCurrentState != STATE_IDLE &&
mCurrentState != STATE_PREPARING);
}
@Override
public boolean canPause() {
return mCanPause;
}
@Override
public boolean canSeekBackward() {
return mCanSeekBack;
}
@Override
public boolean canSeekForward() {
return mCanSeekForward;
}
@Override
public int getAudioSessionId() {
return 0;
}
// REMOVED: getAudioSessionId();
// REMOVED: onAttachedToWindow();
// REMOVED: onDetachedFromWindow();
// REMOVED: onLayout();
// REMOVED: draw();
// REMOVED: measureAndLayoutSubtitleWidget();
// REMOVED: setSubtitleWidget();
// REMOVED: getSubtitleLooper();
//-------------------------
// Extend: Aspect Ratio
//-------------------------
private static final int[] s_allAspectRatio = {
IRenderView.AR_ASPECT_FIT_PARENT,
IRenderView.AR_ASPECT_FILL_PARENT,
IRenderView.AR_ASPECT_WRAP_CONTENT,
// IRenderView.AR_MATCH_PARENT,
IRenderView.AR_16_9_FIT_PARENT,
IRenderView.AR_4_3_FIT_PARENT};
private int mCurrentAspectRatioIndex = 0;
private int mCurrentAspectRatio = s_allAspectRatio[0];
public int toggleAspectRatio() {
mCurrentAspectRatioIndex++;
mCurrentAspectRatioIndex %= s_allAspectRatio.length;
mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];
if (mRenderView != null)
mRenderView.setAspectRatio(mCurrentAspectRatio);
return mCurrentAspectRatio;
}
//-------------------------
// Extend: Render
//-------------------------
public static final int RENDER_NONE = 0;
public static final int RENDER_SURFACE_VIEW = 1;
public static final int RENDER_TEXTURE_VIEW = 2;
private List<Integer> mAllRenders = new ArrayList<Integer>();
private int mCurrentRenderIndex = 0;
private int mCurrentRender = RENDER_NONE;
private void initRenders() {
mAllRenders.clear();
if (mSettings.getEnableSurfaceView())
mAllRenders.add(RENDER_SURFACE_VIEW);
if (mSettings.getEnableTextureView() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
mAllRenders.add(RENDER_TEXTURE_VIEW);
if (mSettings.getEnableNoView())
mAllRenders.add(RENDER_NONE);
if (mAllRenders.isEmpty())
mAllRenders.add(RENDER_SURFACE_VIEW);
mCurrentRender = mAllRenders.get(mCurrentRenderIndex);
setRender(mCurrentRender);
}
public int toggleRender() {
mCurrentRenderIndex++;
mCurrentRenderIndex %= mAllRenders.size();
mCurrentRender = mAllRenders.get(mCurrentRenderIndex);
setRender(mCurrentRender);
return mCurrentRender;
}
@NonNull
public static String getRenderText(Context context, int render) {
String text;
switch (render) {
case RENDER_NONE:
text = context.getString(R.string.VideoView_render_none);
break;
case RENDER_SURFACE_VIEW:
text = context.getString(R.string.VideoView_render_surface_view);
break;
case RENDER_TEXTURE_VIEW:
text = context.getString(R.string.VideoView_render_texture_view);
break;
default:
text = context.getString(R.string.N_A);
break;
}
return text;
}
//-------------------------
// Extend: Player
//-------------------------
public int togglePlayer() {
if (mMediaPlayer != null)
mMediaPlayer.release();
if (mRenderView != null)
mRenderView.getView().invalidate();
openVideo();
return mSettings.getPlayer();
}
@NonNull
public static String getPlayerText(Context context, int player) {
String text;
switch (player) {
case Settings.PV_PLAYER__AndroidMediaPlayer:
text = context.getString(R.string.VideoView_player_AndroidMediaPlayer);
break;
case Settings.PV_PLAYER__IjkMediaPlayer:
text = context.getString(R.string.VideoView_player_IjkMediaPlayer);
break;
case Settings.PV_PLAYER__IjkExoMediaPlayer:
text = context.getString(R.string.VideoView_player_IjkExoMediaPlayer);
break;
default:
text = context.getString(R.string.N_A);
break;
}
return text;
}
public IMediaPlayer createPlayer(int playerType) {
IMediaPlayer mediaPlayer = null;
switch (playerType) {
case Settings.PV_PLAYER__IjkExoMediaPlayer: {
IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext);
mediaPlayer = IjkExoMediaPlayer;
}
break;
case Settings.PV_PLAYER__AndroidMediaPlayer: {
AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();
mediaPlayer = androidMediaPlayer;
}
break;
case Settings.PV_PLAYER__IjkMediaPlayer:
default: {
IjkMediaPlayer ijkMediaPlayer = null;
if (mUri != null) {
ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
if (mSettings.getUsingMediaCodec()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
if (mSettings.getUsingMediaCodecAutoRotate()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
}
if (mSettings.getMediaCodecHandleResolutionChange()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
}
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
}
if (mSettings.getUsingOpenSLES()) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
}
String pixelFormat = mSettings.getPixelFormat();
if (TextUtils.isEmpty(pixelFormat)) {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
} else {
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);
}
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
}
mediaPlayer = ijkMediaPlayer;
}
break;
}
if (mSettings.getEnableDetachedSurfaceTextureView()) {
mediaPlayer = new TextureMediaPlayer(mediaPlayer);
}
return mediaPlayer;
}
//-------------------------
// Extend: Background
//-------------------------
private boolean mEnableBackgroundPlay = false;
private void initBackground() {
mEnableBackgroundPlay = mSettings.getEnableBackgroundPlay();
if (mEnableBackgroundPlay) {
MediaPlayerService.intentToStart(getContext());
mMediaPlayer = MediaPlayerService.getMediaPlayer();
if (mHudViewHolder != null)
mHudViewHolder.setMediaPlayer(mMediaPlayer);
}
}
public boolean isBackgroundPlayEnabled() {
return mEnableBackgroundPlay;
}
public void enterBackground() {
MediaPlayerService.setMediaPlayer(mMediaPlayer);
}
public void stopBackgroundPlay() {
MediaPlayerService.setMediaPlayer(null);
}
//-------------------------
// Extend: Background
//-------------------------
public void showMediaInfo() {
if (mMediaPlayer == null)
return;
int selectedVideoTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_VIDEO);
int selectedAudioTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_AUDIO);
int selectedSubtitleTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);
TableLayoutBinder builder = new TableLayoutBinder(getContext());
builder.appendSection(R.string.mi_player);
builder.appendRow2(R.string.mi_player, MediaPlayerCompat.getName(mMediaPlayer));
builder.appendSection(R.string.mi_media);
builder.appendRow2(R.string.mi_resolution, buildResolution(mVideoWidth, mVideoHeight, mVideoSarNum, mVideoSarDen));
builder.appendRow2(R.string.mi_length, buildTimeMilli(mMediaPlayer.getDuration()));
ITrackInfo trackInfos[] = mMediaPlayer.getTrackInfo();
if (trackInfos != null) {
int index = -1;
for (ITrackInfo trackInfo : trackInfos) {
index++;
int trackType = trackInfo.getTrackType();
if (index == selectedVideoTrack) {
builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_video_track));
} else if (index == selectedAudioTrack) {
builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_audio_track));
} else if (index == selectedSubtitleTrack) {
builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_subtitle_track));
} else {
builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index));
}
builder.appendRow2(R.string.mi_type, buildTrackType(trackType));
builder.appendRow2(R.string.mi_language, buildLanguage(trackInfo.getLanguage()));
IMediaFormat mediaFormat = trackInfo.getFormat();
if (mediaFormat == null) {
} else if (mediaFormat instanceof IjkMediaFormat) {
switch (trackType) {
case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:
builder.appendRow2(R.string.mi_codec, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI));
builder.appendRow2(R.string.mi_profile_level, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI));
builder.appendRow2(R.string.mi_pixel_format, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PIXEL_FORMAT_UI));
builder.appendRow2(R.string.mi_resolution, mediaFormat.getString(IjkMediaFormat.KEY_IJK_RESOLUTION_UI));
builder.appendRow2(R.string.mi_frame_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_FRAME_RATE_UI));
builder.appendRow2(R.string.mi_bit_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI));
break;
case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:
builder.appendRow2(R.string.mi_codec, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI));
builder.appendRow2(R.string.mi_profile_level, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI));
builder.appendRow2(R.string.mi_sample_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_SAMPLE_RATE_UI));
builder.appendRow2(R.string.mi_channels, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CHANNEL_UI));
builder.appendRow2(R.string.mi_bit_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI));
break;
default:
break;
}
}
}
}
AlertDialog.Builder adBuilder = builder.buildAlertDialogBuilder();
adBuilder.setTitle(R.string.media_information);
adBuilder.setNegativeButton(R.string.close, null);
adBuilder.show();
}
private String buildResolution(int width, int height, int sarNum, int sarDen) {
StringBuilder sb = new StringBuilder();
sb.append(width);
sb.append(" x ");
sb.append(height);
if (sarNum > 1 || sarDen > 1) {
sb.append("[");
sb.append(sarNum);
sb.append(":");
sb.append(sarDen);
sb.append("]");
}
return sb.toString();
}
private String buildTimeMilli(long duration) {
long total_seconds = duration / 1000;
long hours = total_seconds / 3600;
long minutes = (total_seconds % 3600) / 60;
long seconds = total_seconds % 60;
if (duration <= 0) {
return "--:--";
}
if (hours >= 100) {
return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds);
} else if (hours > 0) {
return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);
} else {
return String.format(Locale.US, "%02d:%02d", minutes, seconds);
}
}
private String buildTrackType(int type) {
Context context = getContext();
switch (type) {
case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:
return context.getString(R.string.TrackType_video);
case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:
return context.getString(R.string.TrackType_audio);
case ITrackInfo.MEDIA_TRACK_TYPE_SUBTITLE:
return context.getString(R.string.TrackType_subtitle);
case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT:
return context.getString(R.string.TrackType_timedtext);
case ITrackInfo.MEDIA_TRACK_TYPE_METADATA:
return context.getString(R.string.TrackType_metadata);
case ITrackInfo.MEDIA_TRACK_TYPE_UNKNOWN:
default:
return context.getString(R.string.TrackType_unknown);
}
}
private String buildLanguage(String language) {
if (TextUtils.isEmpty(language))
return "und";
return language;
}
public ITrackInfo[] getTrackInfo() {
if (mMediaPlayer == null)
return null;
return mMediaPlayer.getTrackInfo();
}
public void selectTrack(int stream) {
MediaPlayerCompat.selectTrack(mMediaPlayer, stream);
}
public void deselectTrack(int stream) {
MediaPlayerCompat.deselectTrack(mMediaPlayer, stream);
}
public int getSelectedTrack(int trackType) {
return MediaPlayerCompat.getSelectedTrack(mMediaPlayer, trackType);
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.view.View;
import android.widget.MediaController;
public interface IMediaController {
void hide();
boolean isShowing();
void setAnchorView(View view);
void setEnabled(boolean enabled);
void setMediaPlayer(MediaController.MediaPlayerControl player);
void show(int timeout);
void show();
//----------
// Extends
//----------
void showOnce(View view);
}
package com.example.k.bilibiliapi.video;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.SparseArray;
import android.view.View;
import android.widget.TableLayout;
import com.example.k.bilibiliapi.R;
import java.util.Locale;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
import tv.danmaku.ijk.media.player.MediaPlayerProxy;
public class InfoHudViewHolder {
private TableLayoutBinder mTableLayoutBinder;
private SparseArray<View> mRowMap = new SparseArray<View>();
private IMediaPlayer mMediaPlayer;
private long mLoadCost = 0;
private long mSeekCost = 0;
public InfoHudViewHolder(Context context, TableLayout tableLayout) {
mTableLayoutBinder = new TableLayoutBinder(context, tableLayout);
}
private void appendSection(int nameId) {
mTableLayoutBinder.appendSection(nameId);
}
private void appendRow(int nameId) {
View rowView = mTableLayoutBinder.appendRow2(nameId, null);
mRowMap.put(nameId, rowView);
}
private void setRowValue(int id, String value) {
View rowView = mRowMap.get(id);
if (rowView == null) {
rowView = mTableLayoutBinder.appendRow2(id, value);
mRowMap.put(id, rowView);
} else {
mTableLayoutBinder.setValueText(rowView, value);
}
}
public void setMediaPlayer(IMediaPlayer mp) {
mMediaPlayer = mp;
if (mMediaPlayer != null) {
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500);
} else {
mHandler.removeMessages(MSG_UPDATE_HUD);
}
}
private static String formatedDurationMilli(long duration) {
if (duration >= 1000) {
return String.format(Locale.US, "%.2f sec", ((float)duration) / 1000);
} else {
return String.format(Locale.US, "%d msec", duration);
}
}
private static String formatedSpeed(long bytes,long elapsed_milli) {
if (elapsed_milli <= 0) {
return "0 B/s";
}
if (bytes <= 0) {
return "0 B/s";
}
float bytes_per_sec = ((float)bytes) * 1000.f / elapsed_milli;
if (bytes_per_sec >= 1000 * 1000) {
return String.format(Locale.US, "%.2f MB/s", ((float)bytes_per_sec) / 1000 / 1000);
} else if (bytes_per_sec >= 1000) {
return String.format(Locale.US, "%.1f KB/s", ((float)bytes_per_sec) / 1000);
} else {
return String.format(Locale.US, "%d B/s", (long)bytes_per_sec);
}
}
public void updateLoadCost(long time) {
mLoadCost = time;
}
public void updateSeekCost(long time) {
mSeekCost = time;
}
private static String formatedSize(long bytes) {
if (bytes >= 100 * 1000) {
return String.format(Locale.US, "%.2f MB", ((float)bytes) / 1000 / 1000);
} else if (bytes >= 100) {
return String.format(Locale.US, "%.1f KB", ((float)bytes) / 1000);
} else {
return String.format(Locale.US, "%d B", bytes);
}
}
private static final int MSG_UPDATE_HUD = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_HUD: {
InfoHudViewHolder holder = InfoHudViewHolder.this;
IjkMediaPlayer mp = null;
if (mMediaPlayer == null)
break;
if (mMediaPlayer instanceof IjkMediaPlayer) {
mp = (IjkMediaPlayer) mMediaPlayer;
} else if (mMediaPlayer instanceof MediaPlayerProxy) {
MediaPlayerProxy proxy = (MediaPlayerProxy) mMediaPlayer;
IMediaPlayer internal = proxy.getInternalMediaPlayer();
if (internal != null && internal instanceof IjkMediaPlayer)
mp = (IjkMediaPlayer) internal;
}
if (mp == null)
break;
int vdec = mp.getVideoDecoder();
switch (vdec) {
case IjkMediaPlayer.FFP_PROPV_DECODER_AVCODEC:
setRowValue(R.string.vdec, "avcodec");
break;
case IjkMediaPlayer.FFP_PROPV_DECODER_MEDIACODEC:
setRowValue(R.string.vdec, "MediaCodec");
break;
default:
setRowValue(R.string.vdec, "");
break;
}
float fpsOutput = mp.getVideoOutputFramesPerSecond();
float fpsDecode = mp.getVideoDecodeFramesPerSecond();
setRowValue(R.string.fps, String.format(Locale.US, "%.2f / %.2f", fpsDecode, fpsOutput));
long videoCachedDuration = mp.getVideoCachedDuration();
long audioCachedDuration = mp.getAudioCachedDuration();
long videoCachedBytes = mp.getVideoCachedBytes();
long audioCachedBytes = mp.getAudioCachedBytes();
long tcpSpeed = mp.getTcpSpeed();
long bitRate = mp.getBitRate();
long seekLoadDuration = mp.getSeekLoadDuration();
setRowValue(R.string.v_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(videoCachedDuration), formatedSize(videoCachedBytes)));
setRowValue(R.string.a_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(audioCachedDuration), formatedSize(audioCachedBytes)));
setRowValue(R.string.load_cost, String.format(Locale.US, "%d ms", mLoadCost));
setRowValue(R.string.seek_cost, String.format(Locale.US, "%d ms", mSeekCost));
setRowValue(R.string.seek_load_cost, String.format(Locale.US, "%d ms", seekLoadDuration));
setRowValue(R.string.tcp_speed, String.format(Locale.US, "%s", formatedSpeed(tcpSpeed, 1000)));
setRowValue(R.string.bit_rate, String.format(Locale.US, "%.2f kbs", bitRate/1000f));
mHandler.removeMessages(MSG_UPDATE_HUD);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500);
}
}
}
};
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.graphics.SurfaceTexture;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import tv.danmaku.ijk.media.player.IMediaPlayer;
public interface IRenderView {
int AR_ASPECT_FIT_PARENT = 0; // without clip
int AR_ASPECT_FILL_PARENT = 1; // may clip
int AR_ASPECT_WRAP_CONTENT = 2;
int AR_MATCH_PARENT = 3;
int AR_16_9_FIT_PARENT = 4;
int AR_4_3_FIT_PARENT = 5;
View getView();
boolean shouldWaitForResize();
void setVideoSize(int videoWidth, int videoHeight);
void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
void setVideoRotation(int degree);
void setAspectRatio(int aspectRatio);
void addRenderCallback(@NonNull IRenderCallback callback);
void removeRenderCallback(@NonNull IRenderCallback callback);
interface ISurfaceHolder {
void bindToMediaPlayer(IMediaPlayer mp);
@NonNull
IRenderView getRenderView();
@Nullable
SurfaceHolder getSurfaceHolder();
@Nullable
Surface openSurface();
@Nullable
SurfaceTexture getSurfaceTexture();
}
interface IRenderCallback {
/**
* @param holder
* @param width could be 0
* @param height could be 0
*/
void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
/**
* @param holder
* @param format could be 0
* @param width
* @param height
*/
void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import com.example.k.bilibiliapi.R;
import java.lang.ref.WeakReference;
public final class MeasureHelper {
private WeakReference<View> mWeakView;
private int mVideoWidth;
private int mVideoHeight;
private int mVideoSarNum;
private int mVideoSarDen;
private int mVideoRotationDegree;
private int mMeasuredWidth;
private int mMeasuredHeight;
private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
public MeasureHelper(View view) {
mWeakView = new WeakReference<View>(view);
}
public View getView() {
if (mWeakView == null)
return null;
return mWeakView.get();
}
public void setVideoSize(int videoWidth, int videoHeight) {
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
}
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
mVideoSarNum = videoSarNum;
mVideoSarDen = videoSarDen;
}
public void setVideoRotation(int videoRotationDegree) {
mVideoRotationDegree = videoRotationDegree;
}
/**
* Must be called by View.onMeasure(int, int)
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
// + MeasureSpec.toString(heightMeasureSpec) + ")");
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
int tempSpec = widthMeasureSpec;
widthMeasureSpec = heightMeasureSpec;
heightMeasureSpec = tempSpec;
}
int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
width = widthMeasureSpec;
height = heightMeasureSpec;
} else if (mVideoWidth > 0 && mVideoHeight > 0) {
int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
float displayAspectRatio;
switch (mCurrentAspectRatio) {
case IRenderView.AR_16_9_FIT_PARENT:
displayAspectRatio = 16.0f / 9.0f;
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
displayAspectRatio = 1.0f / displayAspectRatio;
break;
case IRenderView.AR_4_3_FIT_PARENT:
displayAspectRatio = 4.0f / 3.0f;
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
displayAspectRatio = 1.0f / displayAspectRatio;
break;
case IRenderView.AR_ASPECT_FIT_PARENT:
case IRenderView.AR_ASPECT_FILL_PARENT:
case IRenderView.AR_ASPECT_WRAP_CONTENT:
default:
displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
if (mVideoSarNum > 0 && mVideoSarDen > 0)
displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
break;
}
boolean shouldBeWider = displayAspectRatio > specAspectRatio;
switch (mCurrentAspectRatio) {
case IRenderView.AR_ASPECT_FIT_PARENT:
case IRenderView.AR_16_9_FIT_PARENT:
case IRenderView.AR_4_3_FIT_PARENT:
if (shouldBeWider) {
// too wide, fix width
width = widthSpecSize;
height = (int) (width / displayAspectRatio);
} else {
// too high, fix height
height = heightSpecSize;
width = (int) (height * displayAspectRatio);
}
break;
case IRenderView.AR_ASPECT_FILL_PARENT:
if (shouldBeWider) {
// not high enough, fix height
height = heightSpecSize;
width = (int) (height * displayAspectRatio);
} else {
// not wide enough, fix width
width = widthSpecSize;
height = (int) (width / displayAspectRatio);
}
break;
case IRenderView.AR_ASPECT_WRAP_CONTENT:
default:
if (shouldBeWider) {
// too wide, fix width
width = Math.min(mVideoWidth, widthSpecSize);
height = (int) (width / displayAspectRatio);
} else {
// too high, fix height
height = Math.min(mVideoHeight, heightSpecSize);
width = (int) (height * displayAspectRatio);
}
break;
}
} else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.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 == View.MeasureSpec.EXACTLY) {
// only the width is fixed, adjust the height to match aspect ratio if possible
width = widthSpecSize;
height = width * mVideoHeight / mVideoWidth;
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
// couldn't match aspect ratio within the constraints
height = heightSpecSize;
}
} else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
// only the height is fixed, adjust the width to match aspect ratio if possible
height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
if (widthSpecMode == View.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 == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
// too tall, decrease both width and height
height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
}
if (widthSpecMode == View.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
}
mMeasuredWidth = width;
mMeasuredHeight = height;
}
public int getMeasuredWidth() {
return mMeasuredWidth;
}
public int getMeasuredHeight() {
return mMeasuredHeight;
}
public void setAspectRatio(int aspectRatio) {
mCurrentAspectRatio = aspectRatio;
}
@NonNull
public static String getAspectRatioText(Context context, int aspectRatio) {
String text;
switch (aspectRatio) {
case IRenderView.AR_ASPECT_FIT_PARENT:
text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
break;
case IRenderView.AR_ASPECT_FILL_PARENT:
text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
break;
case IRenderView.AR_ASPECT_WRAP_CONTENT:
text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
break;
case IRenderView.AR_MATCH_PARENT:
text = context.getString(R.string.VideoView_ar_match_parent);
break;
case IRenderView.AR_16_9_FIT_PARENT:
text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
break;
case IRenderView.AR_4_3_FIT_PARENT:
text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
break;
default:
text = context.getString(R.string.N_A);
break;
}
return text;
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
import tv.danmaku.ijk.media.player.MediaPlayerProxy;
import tv.danmaku.ijk.media.player.TextureMediaPlayer;
public class MediaPlayerCompat {
public static String getName(IMediaPlayer mp) {
if (mp == null) {
return "null";
} else if (mp instanceof TextureMediaPlayer) {
StringBuilder sb = new StringBuilder("TextureMediaPlayer <");
IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer();
if (internalMediaPlayer == null) {
sb.append("null>");
} else {
sb.append(internalMediaPlayer.getClass().getSimpleName());
sb.append(">");
}
return sb.toString();
} else {
return mp.getClass().getSimpleName();
}
}
public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) {
IjkMediaPlayer ijkMediaPlayer = null;
if (mp == null) {
return null;
} if (mp instanceof IjkMediaPlayer) {
ijkMediaPlayer = (IjkMediaPlayer) mp;
} else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) {
ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer();
}
return ijkMediaPlayer;
}
public static void selectTrack(IMediaPlayer mp, int stream) {
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
if (ijkMediaPlayer == null)
return;
ijkMediaPlayer.selectTrack(stream);
}
public static void deselectTrack(IMediaPlayer mp, int stream) {
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
if (ijkMediaPlayer == null)
return;
ijkMediaPlayer.deselectTrack(stream);
}
public static int getSelectedTrack(IMediaPlayer mp, int trackType) {
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
if (ijkMediaPlayer == null)
return -1;
return ijkMediaPlayer.getSelectedTrack(trackType);
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import tv.danmaku.ijk.media.player.IMediaPlayer;
public class MediaPlayerService extends Service {
private static IMediaPlayer sMediaPlayer;
public static Intent newIntent(Context context) {
Intent intent = new Intent(context, MediaPlayerService.class);
return intent;
}
public static void intentToStart(Context context) {
context.startService(newIntent(context));
}
public static void intentToStop(Context context) {
context.stopService(newIntent(context));
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static void setMediaPlayer(IMediaPlayer mp) {
if (sMediaPlayer != null && sMediaPlayer != mp) {
if (sMediaPlayer.isPlaying())
sMediaPlayer.stop();
sMediaPlayer.release();
sMediaPlayer = null;
}
sMediaPlayer = mp;
}
public static IMediaPlayer getMediaPlayer() {
return sMediaPlayer;
}
}
package com.example.k.bilibiliapi.video;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.media.AudioManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.k.bilibiliapi.R;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
import tv.danmaku.ijk.media.player.pragma.DebugLog;
/**
* Created by yilv on 2018/3/29.
*/
public class PlayerManager {
/**
* 可能會剪裁,保持原視訊的大小,顯示在中心,當原視訊的大小超過view的大小超過部分裁剪處理
*/
public static final String SCALETYPE_FITPARENT="fitParent";
/**
* 可能會剪裁,等比例放大視訊,直到填滿View爲止,超過View的部分作裁剪處理
*/
public static final String SCALETYPE_FILLPARENT="fillParent";
/**
* 將視訊的內容完整居中顯示,如果視訊大於view,則按比例縮視訊直到完全顯示在view中
*/
public static final String SCALETYPE_WRAPCONTENT="wrapContent";
/**
* 不剪裁,非等比例拉伸畫面填滿整個View
*/
public static final String SCALETYPE_FITXY="fitXY";
/**
* 不剪裁,非等比例拉伸畫面到16:9,並完全顯示在View中
*/
public static final String SCALETYPE_16_9="16:9";
/**
* 不剪裁,非等比例拉伸畫面到4:3,並完全顯示在View中
*/
public static final String SCALETYPE_4_3="4:3";
/**
* 狀態常數
*/
private final int STATUS_ERROR=-1;
private final int STATUS_IDLE=0;
private final int STATUS_LOADING=1;
private final int STATUS_PLAYING=2;
private final int STATUS_PAUSE=3;
private final int STATUS_COMPLETED=4;
private final Activity activity;
private final IjkVideoView videoView;
private final AudioManager audioManager;
public GestureDetector gestureDetector;
private boolean playerSupport;
private boolean isLive = false;//是否爲直播
private boolean fullScreenOnly;
private boolean portrait;
private final int mMaxVolume;
private int screenWidthPixels;
private int currentPosition;
private int status=STATUS_IDLE;
private long pauseTime;
private String url;
private float brightness=-1;
private int volume=-1;
private long newPosition = -1;
private long defaultRetryTime=5000;
private OrientationEventListener orientationEventListener;
private PlayerStateListener playerStateListener;
public void setPlayerStateListener(PlayerStateListener playerStateListener) {
this.playerStateListener = playerStateListener;
}
private OnErrorListener onErrorListener=new OnErrorListener() {
@Override
public void onError(int what, int extra) {
}
};
private OnCompleteListener onCompleteListener=new OnCompleteListener() {
@Override
public void onComplete() {
}
};
private OnInfoListener onInfoListener=new OnInfoListener(){
@Override
public void onInfo(int what, int extra) {
}
};
private OnControlPanelVisibilityChangeListener onControlPanelVisibilityChangeListener=new OnControlPanelVisibilityChangeListener() {
@Override
public void change(boolean isShowing) {
}
};
/**
* try to play when error(only for live video)
* @param defaultRetryTime millisecond,0 will stop retry,default is 5000 millisecond
*/
public void setDefaultRetryTime(long defaultRetryTime) {
this.defaultRetryTime = defaultRetryTime;
}
public PlayerManager(final Activity activity) {
try {
IjkMediaPlayer.loadLibrariesOnce(null);
IjkMediaPlayer.native_profileBegin("libijkplayer.so");
playerSupport=true;
} catch (Throwable e) {
Log.e("GiraffePlayer", "loadLibraries error", e);
}
this.activity=activity;
screenWidthPixels = activity.getResources().getDisplayMetrics().widthPixels;
videoView = (IjkVideoView) activity.findViewById(R.id.video_view);
videoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(IMediaPlayer mp) {
statusChange(STATUS_COMPLETED);
onCompleteListener.onComplete();
}
});
videoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
@Override
public boolean onError(IMediaPlayer mp, int what, int extra) {
statusChange(STATUS_ERROR);
onErrorListener.onError(what,extra);
return true;
}
});
videoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(IMediaPlayer mp, int what, int extra) {
switch (what) {
case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
statusChange(STATUS_LOADING);
break;
case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
statusChange(STATUS_PLAYING);
break;
case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
//顯示下載速度
// Toast.show("download rate:" + extra);
break;
case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
statusChange(STATUS_PLAYING);
break;
}
onInfoListener.onInfo(what,extra);
return false;
}
});
audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
mMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
gestureDetector = new GestureDetector(activity, new PlayerGestureListener());
if (fullScreenOnly) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
portrait=getScreenOrientation()== ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
if (!playerSupport) {
DebugLog.e("","播放器不支援此裝置");
}
}
private void statusChange(int newStatus) {
status = newStatus;
if (!isLive && newStatus==STATUS_COMPLETED) {
DebugLog.d("","statusChange STATUS_COMPLETED...");
if (playerStateListener != null){
playerStateListener.onComplete();
}
}else if (newStatus == STATUS_ERROR) {
DebugLog.d("","statusChange STATUS_ERROR...");
if (playerStateListener != null){
playerStateListener.onError();
}
} else if(newStatus==STATUS_LOADING){
// $.id(R.id.app_video_loading).visible();
if (playerStateListener != null){
playerStateListener.onLoading();
}
DebugLog.d("","statusChange STATUS_LOADING...");
} else if (newStatus == STATUS_PLAYING) {
DebugLog.d("","statusChange STATUS_PLAYING...");
if (playerStateListener != null){
playerStateListener.onPlay();
}
}
}
public void onPause() {
pauseTime= System.currentTimeMillis();
if (status==STATUS_PLAYING) {
videoView.pause();
if (!isLive) {
currentPosition = videoView.getCurrentPosition();
}
}
}
public void onResume() {
pauseTime=0;
if (status==STATUS_PLAYING) {
if (isLive) {
videoView.seekTo(0);
} else {
if (currentPosition>0) {
videoView.seekTo(currentPosition);
}
}
videoView.start();
}
}
public void onDestroy() {
orientationEventListener.disable();
videoView.stopPlayback();
}
public void play(String url) {
this.url = url;
if (playerSupport) {
videoView.setVideoPath(url);
videoView.start();
}
}
private String generateTime(long time) {
int totalSeconds = (int) (time / 1000);
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
}
private int getScreenOrientation() {
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
int orientation;
// if the device's natural orientation is portrait:
if ((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && height > width ||
(rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && width > height) {
switch (rotation) {
case Surface.ROTATION_0:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_90:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_180:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
case Surface.ROTATION_270:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
default:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
}
}
// if the device's natural orientation is landscape or if the device
// is square:
else {
switch (rotation) {
case Surface.ROTATION_0:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_90:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_180:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
case Surface.ROTATION_270:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
default:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
}
}
return orientation;
}
/**
* 滑動改變聲音大小
*
* @param percent
*/
private void onVolumeSlide(float percent) {
if (volume == -1) {
volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
if (volume < 0)
volume = 0;
}
int index = (int) (percent * mMaxVolume) + volume;
if (index > mMaxVolume) {
index = mMaxVolume;
} else if (index < 0){
index = 0;
}
// 變更聲音
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);
// 變更進度條
int i = (int) (index * 1.0 / mMaxVolume * 100);
String s = i + "%";
if (i == 0) {
s = "off";
}
DebugLog.d("","onVolumeSlide:"+s);
}
private void onProgressSlide(float percent) {
long position = videoView.getCurrentPosition();
long duration = videoView.getDuration();
long deltaMax = Math.min(100 * 1000, duration - position);
long delta = (long) (deltaMax * percent);
newPosition = delta + position;
if (newPosition > duration) {
newPosition = duration;
} else if (newPosition <= 0) {
newPosition=0;
delta=-position;
}
int showDelta = (int) delta / 1000;
if (showDelta != 0) {
String text = showDelta > 0 ? ("+" + showDelta) : "" + showDelta;
DebugLog.d("","onProgressSlide:" + text);
}
}
/**
* 滑動改變亮度
*
* @param percent
*/
private void onBrightnessSlide(float percent) {
if (brightness < 0) {
brightness = activity.getWindow().getAttributes().screenBrightness;
if (brightness <= 0.00f){
brightness = 0.50f;
}else if (brightness < 0.01f){
brightness = 0.01f;
}
}
DebugLog.d("","brightness:"+brightness+",percent:"+ percent);
WindowManager.LayoutParams lpa = activity.getWindow().getAttributes();
lpa.screenBrightness = brightness + percent;
if (lpa.screenBrightness > 1.0f){
lpa.screenBrightness = 1.0f;
}else if (lpa.screenBrightness < 0.01f){
lpa.screenBrightness = 0.01f;
}
activity.getWindow().setAttributes(lpa);
}
public void setFullScreenOnly(boolean fullScreenOnly) {
this.fullScreenOnly = fullScreenOnly;
tryFullScreen(fullScreenOnly);
if (fullScreenOnly) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}
private void tryFullScreen(boolean fullScreen) {
if (activity instanceof AppCompatActivity) {
ActionBar supportActionBar = ((AppCompatActivity) activity).getSupportActionBar();
if (supportActionBar != null) {
if (fullScreen) {
supportActionBar.hide();
} else {
supportActionBar.show();
}
}
}
setFullScreen(fullScreen);
}
private void setFullScreen(boolean fullScreen) {
if (activity != null) {
WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
if (fullScreen) {
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
activity.getWindow().setAttributes(attrs);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
} else {
attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().setAttributes(attrs);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
}
}
/**
* <pre>
* fitParent:可能會剪裁,保持原視訊的大小,顯示在中心,當原視訊的大小超過view的大小超過部分裁剪處理
* fillParent:可能會剪裁,等比例放大視訊,直到填滿View爲止,超過View的部分作裁剪處理
* wrapContent:將視訊的內容完整居中顯示,如果視訊大於view,則按比例縮視訊直到完全顯示在view中
* fitXY:不剪裁,非等比例拉伸畫面填滿整個View
* 16:9:不剪裁,非等比例拉伸畫面到16:9,並完全顯示在View中
* 4:3:不剪裁,非等比例拉伸畫面到4:3,並完全顯示在View中
* </pre>
* @param scaleType
*/
public void setScaleType(String scaleType) {
if (SCALETYPE_FITPARENT.equals(scaleType)) {
videoView.setAspectRatio(IRenderView.AR_ASPECT_FIT_PARENT);
}else if (SCALETYPE_FILLPARENT.equals(scaleType)) {
videoView.setAspectRatio(IRenderView.AR_ASPECT_FILL_PARENT);
}else if (SCALETYPE_WRAPCONTENT.equals(scaleType)) {
videoView.setAspectRatio(IRenderView.AR_ASPECT_WRAP_CONTENT);
}else if (SCALETYPE_FITXY.equals(scaleType)) {
videoView.setAspectRatio(IRenderView.AR_MATCH_PARENT);
}else if (SCALETYPE_16_9.equals(scaleType)) {
videoView.setAspectRatio(IRenderView.AR_16_9_FIT_PARENT);
}else if (SCALETYPE_4_3.equals(scaleType)) {
videoView.setAspectRatio(IRenderView.AR_4_3_FIT_PARENT);
}
}
public void start() {
videoView.start();
}
public void pause() {
videoView.pause();
}
public boolean onBackPressed() {
if (!fullScreenOnly && getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
return true;
}
return false;
}
class Query {
private final Activity activity;
private View view;
public Query(Activity activity) {
this.activity=activity;
}
public Query id(int id) {
view = activity.findViewById(id);
return this;
}
public Query image(int resId) {
if (view instanceof ImageView) {
((ImageView) view).setImageResource(resId);
}
return this;
}
public Query visible() {
if (view != null) {
view.setVisibility(View.VISIBLE);
}
return this;
}
public Query gone() {
if (view != null) {
view.setVisibility(View.GONE);
}
return this;
}
public Query invisible() {
if (view != null) {
view.setVisibility(View.INVISIBLE);
}
return this;
}
public Query clicked(View.OnClickListener handler) {
if (view != null) {
view.setOnClickListener(handler);
}
return this;
}
public Query text(CharSequence text) {
if (view!=null && view instanceof TextView) {
((TextView) view).setText(text);
}
return this;
}
public Query visibility(int visible) {
if (view != null) {
view.setVisibility(visible);
}
return this;
}
private void size(boolean width, int n, boolean dip){
if(view != null){
ViewGroup.LayoutParams lp = view.getLayoutParams();
if(n > 0 && dip){
n = dip2pixel(activity, n);
}
if(width){
lp.width = n;
}else{
lp.height = n;
}
view.setLayoutParams(lp);
}
}
public void height(int height, boolean dip) {
size(false,height,dip);
}
public int dip2pixel(Context context, float n){
int value = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, n, context.getResources().getDisplayMetrics());
return value;
}
public float pixel2dip(Context context, float n){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float dp = n / (metrics.densityDpi / 160f);
return dp;
}
}
public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener {
private boolean firstTouch;
private boolean volumeControl;
private boolean toSeek;
/**
* 雙擊
*/
@Override
public boolean onDoubleTap(MotionEvent e) {
videoView.toggleAspectRatio();
return true;
}
@Override
public boolean onDown(MotionEvent e) {
firstTouch = true;
return super.onDown(e);
}
/**
* 滑動
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
float mOldX = e1.getX(), mOldY = e1.getY();
float deltaY = mOldY - e2.getY();
float deltaX = mOldX - e2.getX();
if (firstTouch) {
toSeek = Math.abs(distanceX) >= Math.abs(distanceY);
volumeControl=mOldX > screenWidthPixels * 0.5f;
firstTouch = false;
}
if (toSeek) {
if (!isLive) {
onProgressSlide(-deltaX / videoView.getWidth());
}
} else {
float percent = deltaY / videoView.getHeight();
if (volumeControl) {
onVolumeSlide(percent);
} else {
onBrightnessSlide(percent);
}
}
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
}
/**
* is player support this device
* @return
*/
public boolean isPlayerSupport() {
return playerSupport;
}
/**
* 是否正在播放
* @return
*/
public boolean isPlaying() {
return videoView!=null?videoView.isPlaying():false;
}
public void stop(){
videoView.stopPlayback();
}
public int getCurrentPosition(){
return videoView.getCurrentPosition();
}
public IjkVideoView getVideoView(){
if (videoView != null){
return videoView;
}
return null;
}
/**
* get video duration
* @return
*/
public int getDuration(){
return videoView.getDuration();
}
public PlayerManager playInFullScreen(boolean fullScreen){
if (fullScreen) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
return this;
}
public PlayerManager onError(OnErrorListener onErrorListener) {
this.onErrorListener = onErrorListener;
return this;
}
public PlayerManager onComplete(OnCompleteListener onCompleteListener) {
this.onCompleteListener = onCompleteListener;
return this;
}
public PlayerManager onInfo(OnInfoListener onInfoListener) {
this.onInfoListener = onInfoListener;
return this;
}
public PlayerManager onControlPanelVisibilityChange(OnControlPanelVisibilityChangeListener listener){
this.onControlPanelVisibilityChangeListener = listener;
return this;
}
/**
* set is live (can't seek forward)
* @param isLive
* @return
*/
public PlayerManager live(boolean isLive) {
this.isLive = isLive;
return this;
}
public PlayerManager toggleAspectRatio(){
if (videoView != null) {
videoView.toggleAspectRatio();
}
return this;
}
public interface PlayerStateListener{
void onComplete();
void onError();
void onLoading();
void onPlay();
}
public interface OnErrorListener{
void onError(int what, int extra);
}
public interface OnCompleteListener{
void onComplete();
}
public interface OnControlPanelVisibilityChangeListener{
void change(boolean isShowing);
}
public interface OnInfoListener{
void onInfo(int what, int extra);
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.example.k.bilibiliapi.R;
public class Settings {
private Context mAppContext;
private SharedPreferences mSharedPreferences;
public static final int PV_PLAYER__Auto = 0;
public static final int PV_PLAYER__AndroidMediaPlayer = 1;
public static final int PV_PLAYER__IjkMediaPlayer = 2;
public static final int PV_PLAYER__IjkExoMediaPlayer = 3;
public Settings(Context context) {
mAppContext = context.getApplicationContext();
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);
}
public boolean getEnableBackgroundPlay() {
String key = mAppContext.getString(R.string.pref_key_enable_background_play);
return mSharedPreferences.getBoolean(key, false);
}
public int getPlayer() {
String key = mAppContext.getString(R.string.pref_key_player);
String value = mSharedPreferences.getString(key, "");
try {
return Integer.valueOf(value).intValue();
} catch (NumberFormatException e) {
return 0;
}
}
public boolean getUsingMediaCodec() {
String key = mAppContext.getString(R.string.pref_key_using_media_codec);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getUsingMediaCodecAutoRotate() {
String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getMediaCodecHandleResolutionChange() {
String key = mAppContext.getString(R.string.pref_key_media_codec_handle_resolution_change);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getUsingOpenSLES() {
String key = mAppContext.getString(R.string.pref_key_using_opensl_es);
return mSharedPreferences.getBoolean(key, false);
}
public String getPixelFormat() {
String key = mAppContext.getString(R.string.pref_key_pixel_format);
return mSharedPreferences.getString(key, "");
}
public boolean getEnableNoView() {
String key = mAppContext.getString(R.string.pref_key_enable_no_view);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getEnableSurfaceView() {
String key = mAppContext.getString(R.string.pref_key_enable_surface_view);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getEnableTextureView() {
String key = mAppContext.getString(R.string.pref_key_enable_texture_view);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getEnableDetachedSurfaceTextureView() {
String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture);
return mSharedPreferences.getBoolean(key, false);
}
public boolean getUsingMediaDataSource() {
String key = mAppContext.getString(R.string.pref_key_using_mediadatasource);
return mSharedPreferences.getBoolean(key, false);
}
public String getLastDirectory() {
String key = mAppContext.getString(R.string.pref_key_last_directory);
return mSharedPreferences.getString(key, "/");
}
public void setLastDirectory(String path) {
String key = mAppContext.getString(R.string.pref_key_last_directory);
mSharedPreferences.edit().putString(key, path).apply();
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
public class SurfaceRenderView extends SurfaceView implements IRenderView {
private MeasureHelper mMeasureHelper;
public SurfaceRenderView(Context context) {
super(context);
initView(context);
}
public SurfaceRenderView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
private void initView(Context context) {
mMeasureHelper = new MeasureHelper(this);
mSurfaceCallback = new SurfaceCallback(this);
getHolder().addCallback(mSurfaceCallback);
//noinspection deprecation
getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
@Override
public View getView() {
return this;
}
@Override
public boolean shouldWaitForResize() {
return true;
}
//--------------------
// Layout & Measure
//--------------------
@Override
public void setVideoSize(int videoWidth, int videoHeight) {
if (videoWidth > 0 && videoHeight > 0) {
mMeasureHelper.setVideoSize(videoWidth, videoHeight);
getHolder().setFixedSize(videoWidth, videoHeight);
requestLayout();
}
}
@Override
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
if (videoSarNum > 0 && videoSarDen > 0) {
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
requestLayout();
}
}
@Override
public void setVideoRotation(int degree) {
Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");
}
@Override
public void setAspectRatio(int aspectRatio) {
mMeasureHelper.setAspectRatio(aspectRatio);
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (this.getWidth() != 0) {
setMeasuredDimension(this.getWidth(), this.getHeight());
return;
}
int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width, height);
}
//--------------------
// SurfaceViewHolder
//--------------------
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
private SurfaceRenderView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView,
@Nullable SurfaceHolder surfaceHolder) {
mSurfaceView = surfaceView;
mSurfaceHolder = surfaceHolder;
}
public void bindToMediaPlayer(IMediaPlayer mp) {
if (mp != null) {
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
(mp instanceof ISurfaceTextureHolder)) {
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
textureHolder.setSurfaceTexture(null);
}
mp.setDisplay(mSurfaceHolder);
}
}
@NonNull
@Override
public IRenderView getRenderView() {
return mSurfaceView;
}
@Nullable
@Override
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
@Nullable
@Override
public SurfaceTexture getSurfaceTexture() {
return null;
}
@Nullable
@Override
public Surface openSurface() {
if (mSurfaceHolder == null)
return null;
return mSurfaceHolder.getSurface();
}
}
//-------------------------
// SurfaceHolder.Callback
//-------------------------
@Override
public void addRenderCallback(IRenderCallback callback) {
mSurfaceCallback.addRenderCallback(callback);
}
@Override
public void removeRenderCallback(IRenderCallback callback) {
mSurfaceCallback.removeRenderCallback(callback);
}
private SurfaceCallback mSurfaceCallback;
private static final class SurfaceCallback implements SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder;
private boolean mIsFormatChanged;
private int mFormat;
private int mWidth;
private int mHeight;
private WeakReference<SurfaceRenderView> mWeakSurfaceView;
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {
mWeakSurfaceView = new WeakReference<SurfaceRenderView>(surfaceView);
}
public void addRenderCallback(@NonNull IRenderCallback callback) {
mRenderCallbackMap.put(callback, callback);
ISurfaceHolder surfaceHolder = null;
if (mSurfaceHolder != null) {
if (surfaceHolder == null)
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
}
if (mIsFormatChanged) {
if (surfaceHolder == null)
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);
}
}
public void removeRenderCallback(@NonNull IRenderCallback callback) {
mRenderCallbackMap.remove(callback);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceHolder = holder;
mIsFormatChanged = false;
mFormat = 0;
mWidth = 0;
mHeight = 0;
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mSurfaceHolder = null;
mIsFormatChanged = false;
mFormat = 0;
mWidth = 0;
mHeight = 0;
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
renderCallback.onSurfaceDestroyed(surfaceHolder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
mSurfaceHolder = holder;
mIsFormatChanged = true;
mFormat = format;
mWidth = width;
mHeight = height;
// mMeasureHelper.setVideoSize(width, height);
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
}
}
}
//--------------------
// Accessibility
//--------------------
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(SurfaceRenderView.class.getName());
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
info.setClassName(SurfaceRenderView.class.getName());
}
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.content.Context;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TableLayout;
import android.widget.TextView;
import com.example.k.bilibiliapi.R;
public class TableLayoutBinder {
private Context mContext;
public ViewGroup mTableView;
public TableLayout mTableLayout;
public TableLayoutBinder(Context context) {
this(context, R.layout.table_media_info);
}
public TableLayoutBinder(Context context, int layoutResourceId) {
mContext = context;
mTableView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutResourceId, null);
mTableLayout = (TableLayout) mTableView.findViewById(R.id.table);
}
public TableLayoutBinder(Context context, TableLayout tableLayout) {
mContext = context;
mTableView = tableLayout;
mTableLayout = tableLayout;
}
public View appendRow1(String name, String value) {
return appendRow(R.layout.table_media_info_row1, name, value);
}
public View appendRow1(int nameId, String value) {
return appendRow1(mContext.getString(nameId), value);
}
public View appendRow2(String name, String value) {
return appendRow(R.layout.table_media_info_row2, name, value);
}
public View appendRow2(int nameId, String value) {
return appendRow2(mContext.getString(nameId), value);
}
public View appendSection(String name) {
return appendRow(R.layout.table_media_info_section, name, null);
}
public View appendSection(int nameId) {
return appendSection(mContext.getString(nameId));
}
public View appendRow(int layoutId, String name, String value) {
ViewGroup rowView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, mTableLayout, false);
setNameValueText(rowView, name, value);
mTableLayout.addView(rowView);
return rowView;
}
public ViewHolder obtainViewHolder(View rowView) {
ViewHolder viewHolder = (ViewHolder) rowView.getTag();
if (viewHolder == null) {
viewHolder = new ViewHolder();
viewHolder.mNameTextView = (TextView) rowView.findViewById(R.id.name);
viewHolder.mValueTextView = (TextView) rowView.findViewById(R.id.value);
rowView.setTag(viewHolder);
}
return viewHolder;
}
public void setNameValueText(View rowView, String name, String value) {
ViewHolder viewHolder = obtainViewHolder(rowView);
viewHolder.setName(name);
viewHolder.setValue(value);
}
public void setValueText(View rowView, String value) {
ViewHolder viewHolder = obtainViewHolder(rowView);
viewHolder.setValue(value);
}
public ViewGroup buildLayout() {
return mTableView;
}
public AlertDialog.Builder buildAlertDialogBuilder() {
AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(mContext);
dlgBuilder.setView(buildLayout());
return dlgBuilder;
}
private static class ViewHolder {
public TextView mNameTextView;
public TextView mValueTextView;
public void setName(String name) {
if (mNameTextView != null) {
mNameTextView.setText(name);
}
}
public void setValue(String value) {
if (mValueTextView != null) {
mValueTextView.setText(value);
}
}
}
}
/*
* Copyright (C) 2015 Bilibili
* Copyright (C) 2015 Zhang Rui <[email protected]>
*
* 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 com.example.k.bilibiliapi.video;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.TextureView;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
import tv.danmaku.ijk.media.player.ISurfaceTextureHost;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class TextureRenderView extends TextureView implements IRenderView {
private static final String TAG = "TextureRenderView";
private MeasureHelper mMeasureHelper;
public TextureRenderView(Context context) {
super(context);
initView(context);
}
public TextureRenderView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
private void initView(Context context) {
mMeasureHelper = new MeasureHelper(this);
mSurfaceCallback = new SurfaceCallback(this);
setSurfaceTextureListener(mSurfaceCallback);
}
@Override
public View getView() {
return this;
}
@Override
public boolean shouldWaitForResize() {
return false;
}
@Override
protected void onDetachedFromWindow() {
mSurfaceCallback.willDetachFromWindow();
super.onDetachedFromWindow();
mSurfaceCallback.didDetachFromWindow();
}
//--------------------
// Layout & Measure
//--------------------
@Override
public void setVideoSize(int videoWidth, int videoHeight) {
if (videoWidth > 0 && videoHeight > 0) {
mMeasureHelper.setVideoSize(videoWidth, videoHeight);
requestLayout();
}
}
@Override
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
if (videoSarNum > 0 && videoSarDen > 0) {
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
requestLayout();
}
}
@Override
public void setVideoRotation(int degree) {
mMeasureHelper.setVideoRotation(degree);
setRotation(degree);
}
@Override
public void setAspectRatio(int aspectRatio) {
mMeasureHelper.setAspectRatio(aspectRatio);
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
}
//--------------------
// TextureViewHolder
//--------------------
public IRenderView.ISurfaceHolder getSurfaceHolder() {
return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback);
}
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
private TextureRenderView mTextureView;
private SurfaceTexture mSurfaceTexture;
private ISurfaceTextureHost mSurfaceTextureHost;
public InternalSurfaceHolder(@NonNull TextureRenderView textureView,
@Nullable SurfaceTexture surfaceTexture,
@NonNull ISurfaceTextureHost surfaceTextureHost) {
mTextureView = textureView;
mSurfaceTexture = surfaceTexture;
mSurfaceTextureHost = surfaceTextureHost;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void bindToMediaPlayer(IMediaPlayer mp) {
if (mp == null)
return;
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
(mp instanceof ISurfaceTextureHolder)) {
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false);
SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture();
if (surfaceTexture != null) {
mTextureView.setSurfaceTexture(surfaceTexture);
} else {
textureHolder.setSurfaceTexture(mSurfaceTexture);
textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback);
}
} else {
mp.setSurface(openSurface());
}
}
@NonNull
@Override
public IRenderView getRenderView() {
return mTextureView;
}
@Nullable
@Override
public SurfaceHolder getSurfaceHolder() {
return null;
}
@Nullable
@Override
public SurfaceTexture getSurfaceTexture() {
return mSurfaceTexture;
}
@Nullable
@Override
public Surface openSurface() {
if (mSurfaceTexture == null)
return null;
return new Surface(mSurfaceTexture);
}
}
//-------------------------
// SurfaceHolder.Callback
//-------------------------
@Override
public void addRenderCallback(IRenderCallback callback) {
mSurfaceCallback.addRenderCallback(callback);
}
@Override
public void removeRenderCallback(IRenderCallback callback) {
mSurfaceCallback.removeRenderCallback(callback);
}
private SurfaceCallback mSurfaceCallback;
private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost {
private SurfaceTexture mSurfaceTexture;
private boolean mIsFormatChanged;
private int mWidth;
private int mHeight;
private boolean mOwnSurfaceTexture = true;
private boolean mWillDetachFromWindow = false;
private boolean mDidDetachFromWindow = false;
private WeakReference<TextureRenderView> mWeakRenderView;
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
public SurfaceCallback(@NonNull TextureRenderView renderView) {
mWeakRenderView = new WeakReference<TextureRenderView>(renderView);
}
public void setOwnSurfaceTexture(boolean ownSurfaceTexture) {
mOwnSurfaceTexture = ownSurfaceTexture;
}
public void addRenderCallback(@NonNull IRenderCallback callback) {
mRenderCallbackMap.put(callback, callback);
ISurfaceHolder surfaceHolder = null;
if (mSurfaceTexture != null) {
if (surfaceHolder == null)
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
}
if (mIsFormatChanged) {
if (surfaceHolder == null)
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);
}
}
public void removeRenderCallback(@NonNull IRenderCallback callback) {
mRenderCallbackMap.remove(callback);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurfaceTexture = surface;
mIsFormatChanged = false;
mWidth = 0;
mHeight = 0;
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
mSurfaceTexture = surface;
mIsFormatChanged = true;
mWidth = width;
mHeight = height;
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height);
}
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mSurfaceTexture = surface;
mIsFormatChanged = false;
mWidth = 0;
mHeight = 0;
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
renderCallback.onSurfaceDestroyed(surfaceHolder);
}
Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture);
return mOwnSurfaceTexture;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
//-------------------------
// ISurfaceTextureHost
//-------------------------
@Override
public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {
if (surfaceTexture == null) {
Log.d(TAG, "releaseSurfaceTexture: null");
} else if (mDidDetachFromWindow) {
if (surfaceTexture != mSurfaceTexture) {
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture");
surfaceTexture.release();
} else if (!mOwnSurfaceTexture) {
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture");
surfaceTexture.release();
} else {
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView");
}
} else if (mWillDetachFromWindow) {
if (surfaceTexture != mSurfaceTexture) {
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture");
surfaceTexture.release();
} else if (!mOwnSurfaceTexture) {
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView");
setOwnSurfaceTexture(true);
} else {
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView");
}
} else {
if (surfaceTexture != mSurfaceTexture) {
Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture");
surfaceTexture.release();
} else if (!mOwnSurfaceTexture) {
Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView");
setOwnSurfaceTexture(true);
} else {
Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView");
}
}
}
public void willDetachFromWindow() {
Log.d(TAG, "willDetachFromWindow()");
mWillDetachFromWindow = true;
}
public void didDetachFromWindow() {
Log.d(TAG, "didDetachFromWindow()");
mDidDetachFromWindow = true;
}
}
//--------------------
// Accessibility
//--------------------
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(TextureRenderView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(TextureRenderView.class.getName());
}
}
這就是所有需要的檔案了。
點選此處下載編譯好的so庫。
在ijkplayerso
資料夾中將libs資料夾中的幾個資料夾複製到自己專案的app的libs資料夾中。
佈局檔案
<FrameLayout
android:layout_width="400dp"
android:layout_height="300dp">
<com.example.k.bilibiliapi.video.IjkVideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.k.bilibiliapi.video.IjkVideoView>
java檔案中
IjkVideoView VideoPlayer=findViewById(R.id.video_view);
VideoPlayer.setAspectRatio(IRenderView.AR_ASPECT_FIT_PARENT);
VideoPlayer.setVideoURI(Uri.parse(url));
VideoPlayer.start();
只有基本的播放,沒有設定緩衝圖,沒有控制按鈕,返回的時候沒有釋放資源,會一直播放停不下來。