最近在做安卓視訊子產品,背景如下:
視訊豎屏播放時,SurfaceView不能對視訊進行翻轉,采用安卓4.0後的TextureView新控件又會容易受到硬體性能影響(包括TextureView在内試過好幾個OpenGL實作的視訊播放控件,其性能都更容易收到性能影響,比如播放幀率較大的視訊時或者有多個圖檔資源不斷切換時會造成卡頓和丢幀),采用Vitamio後可以解決豎屏播放問題,但其性能還是有待優化。
先奉上Vitamio官方幫助文檔位址:Vitamio Android 開發建議
在近一周的修改後,解決了Vitamio視訊架構中以下問題
Vitamio播放邏輯修改
官方給予的Demo其播放邏輯并不強大,但視訊的切換做的很流暢基本不會出現中間黑屏,看了一下,是對MediaPlayer做了處理,在視訊将要播放completion的時候(一般是結束前1秒)會發出一個提醒,應該是在so庫中封裝的吧。為了不每次setDatasource的時候都釋放資源并重新初始化為VideoView.java中做了以下修改:
1.添加了stop()用于停止播放。
public void stop() {
if (mMediaPlayer != null) {
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying() || mMediaPlayer.getCurrentPosition()>) {
mMediaPlayer.stop();
mCurrentState = STATE_IDLE;
}
}
mMediaPlayer.reset();
mTargetState = STATE_IDLE;
}
}
...
因為在官網提供的MediaPlayer中release()函數如下:
public void release() {
stayAwake(false);
updateSurfaceScreenOn();
mOnPreparedListener = null;
mOnBufferingUpdateListener = null;
mOnCompletionListener = null;
mOnSeekCompleteListener = null;
mOnErrorListener = null;
mOnInfoListener = null;
mOnVideoSizeChangedListener = null;
mOnCachingUpdateListener = null;
mOnHWRenderFailedListener = null;
if (mEventHandler != null)
mEventHandler.release();
//mEventHandler = null;
_release();
closeFD();
mInBuffering = false;
mNeedResume = false;
}
在設定資源的時候就可以這樣進行調用了,可以保證其不會總是建立和釋放對象。
/**
* 設定播放資源,設定播放事件
*/
private void videoHandles() {
filePath = this.videos_A.get(curVideoIndex);
try {
synchronized ((ADPageActivity) mContext) {
if(filePath.indexOf("http:")>-){
mUIParser.sfv.setBufferSize( * );
}else if(filePath.indexOf("/")==){
mUIParser.sfv.setBufferSize();
}
if(onPreparedListener == null){
onPreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
// optional need Vitamio 4.0
mediaPlayer.setPlaybackSpeed(f);
}
};
mUIParser.sfv.setOnPreparedListener(onPreparedListener);
}
if(onCompletionListener == null){
onCompletionListener = new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer arg0) {
Log.i(Tag, "MMediaPlayer onCompletion");
playNextVideo();
}
};
mUIParser.sfv.setOnCompletionListener(onCompletionListener);
}
if(onErrorListener == null){
onErrorListener = new OnErrorListener() {
@Override
public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
Log.e(Tag, "MMediaPlayer onError !!");
playNextVideo();
return true;//傳回true,這個回調函數才會起作用,以下為官方解釋:
//True if the method handled the error, false if it didn't. Returning false, or not having an OnErrorListener at all, will cause the OnCompletionListener to be called.
}
};
mUIParser.sfv.setOnErrorListener(onErrorListener);
}
if(filePath.indexOf("http:")>-){
mUIParser.sfv.setVideoURI(Uri.parse(filePath));
}else if(filePath.indexOf("/")==){
mUIParser.sfv.setVideoPath(filePath);
}
// if(mMediaController==null){
// mMediaController = new MediaController(mContext);//執行個體化控制器
// mMediaController.show(2000);//控制器顯示2s後自動隐藏
// mUIParser.sfv.setMediaController(mMediaController);//綁定控制器
// }
mUIParser.sfv.setVideoQuality(MediaPlayer.VIDEOQUALITY_HIGH);//設定播放畫質 高畫質
mUIParser.sfv.requestFocus();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void playNextVideo() {
Log.i(Tag, "curVideoIndex : " + curVideoIndex);
// 網絡播放模式時,播完一條視訊上傳記錄至伺服器
if (sourceMode == )
uploadPlayData(curVideoIndex);
if (videos_A != null && videos_A.size() > ) {
curVideoIndex++;
if (curVideoIndex >= this.videos_A.size()) {
curVideoIndex = ;
// initAdPlayer();
}
filePath = this.videos_A.get(curVideoIndex);
Log.i(Tag, "filePath : " + filePath);
if(filePath.indexOf("http:")>-){
mUIParser.sfv.setVideoURI(Uri.parse(filePath));
}else if(filePath.indexOf("/")==){
mUIParser.sfv.setVideoPath(filePath);
}
}
}
Vitamio播放界面自動比對視訊大小修改為固定視訊控件大小
不論你xml或者設定的播放控件多大,其播放畫面大小會根據視訊實際進行比對,與以下代碼有關:
setVideoLayout(mVideoLayout, mAspectRatio);
...
public void setVideoLayout(int layout, float aspectRatio) {
LayoutParams lp = getLayoutParams();
Pair<Integer, Integer> res = ScreenResolution.getResolution(mContext);
int windowWidth = res.first.intValue(), windowHeight = res.second
.intValue();
float windowRatio = windowWidth / (float) windowHeight;
float videoRatio = aspectRatio <= f ? mVideoAspectRatio
: aspectRatio;
mSurfaceHeight = mVideoHeight;
mSurfaceWidth = mVideoWidth;
if (VIDEO_LAYOUT_ORIGIN == layout && mSurfaceWidth < windowWidth
&& mSurfaceHeight < windowHeight) {
lp.width = (int) (mSurfaceHeight * videoRatio);
lp.height = mSurfaceHeight;
} else if (layout == VIDEO_LAYOUT_ZOOM) {
lp.width = windowRatio > videoRatio ? windowWidth
: (int) (videoRatio * windowHeight);
lp.height = windowRatio < videoRatio ? windowHeight
: (int) (windowWidth / videoRatio);
} else if (layout == VIDEO_LAYOUT_FIT_PARENT) {
ViewGroup parent = (ViewGroup) getParent();
float parentRatio = ((float) parent.getWidth())
/ ((float) parent.getHeight());
lp.width = (parentRatio < videoRatio) ? parent.getWidth() : Math
.round(((float) parent.getHeight()) * videoRatio);
lp.height = (parentRatio > videoRatio) ? parent.getHeight() : Math
.round(((float) parent.getWidth()) / videoRatio);
} else {
boolean full = layout == VIDEO_LAYOUT_STRETCH;
lp.width = (full || windowRatio < videoRatio) ? windowWidth
: (int) (videoRatio * windowHeight);
lp.height = (full || windowRatio > videoRatio) ? windowHeight
: (int) (windowWidth / videoRatio);
}
setLayoutParams(lp);
getHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight);
Log.d("VIDEO: %dx%dx%f, Surface: %dx%d, LP: %dx%d, Window: %dx%dx%f",
mVideoWidth, mVideoHeight, mVideoAspectRatio, mSurfaceWidth,
mSurfaceHeight, lp.width, lp.height, windowWidth, windowHeight,
windowRatio);
mVideoLayout = layout;
mAspectRatio = aspectRatio;
}
還有一些修改,我做了個Demo,供大家參考。目前還有個問題是:
在硬體環境不夠強大的情況下
1.盡管在public void surfaceCreated(SurfaceHolder holder)中設定了mSurfaceHolder.setFormat(PixelFormat.RGBX_8888),但在個别機型上,播放一些視訊還是會有很明顯的花屏現象。應該跟Vitamio的底層so庫有關系。
2.播放幀率較大視訊還是會丢幀,卡頓。
以下是在我修改的基礎上,省去了一些重要修改的Demo
http://download.csdn.net/detail/mengjiangyue/9577932