package tv.danmaku.media; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import org.videolan.vlc.EventManager; import org.videolan.vlc.LibVLC; import org.videolan.vlc.LibVlcLibraryLoader; import org.videolan.vlc.LibVlcMessages; import org.videolan.vlc.LibVlcException; import org.videolan.vlc.events.MediaPlayerBuffering; import org.videolan.vlc.events.MediaPlayerBufferingTotal; import org.videolan.vlc.events.MediaPlayerSeekableChanged; import tv.danmaku.android.util.Assure; import tv.danmaku.android.util.CollectionHelper; import tv.danmaku.android.util.DebugLog; import tv.danmaku.android.util.WeakHandler; import tv.danmaku.media.resource.PlayIndex; import tv.danmaku.media.vsl.LibVlcVideoSegmentListLoader; import tv.danmaku.pragma.Pragma; import android.content.Context; import android.graphics.PixelFormat; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; // 用于原始的libvlc封装 public class LibVlcMediaPlayer extends AbsMediaPlayer implements Handler.Callback { public static final String TAG = LibVlcMediaPlayer.class.getSimpleName(); // audiotrack/opensles public static final String[] sAout_AudioTrack_Native = new String[] { ":aout=android_audiotrack" }; public static final String[] sAout_AudioTrack_Java = new String[] { ":aout=audiotrack_java" }; public static final String[] sAout_OpenSLES = new String[] { ":aout=opensles" }; // HW/SW public static final String[] sCodec_MediaCodec_Iomx = new String[] { ":codec=mediacodec,iomx,all", ":file-caching=1500", ":network-caching=1500" }; public static final String[] sCodec_Iomx = new String[] { ":codec=iomx,all", ":file-caching=1500", ":network-caching=1500" }; public static final String[] sCodec_All = new String[] { ":codec=all", ":avcodec-fast=1" }; // avcodec optimize public static final String[] sAvcodecOptimize_SkipMost = new String[] { ":avcodec-skiploopfilter=4", ":avcodec-skip-frame=1", ":avcodec-skip-idct=1", }; public static final String[] sAvcodecOptimize_SkipLoopfilter_None = new String[] { ":avcodec-skiploopfilter=0" }; public static final String[] sAvcodecOptimize_SkipLoopfilter_NonRef = new String[] { ":avcodec-skiploopfilter=1" }; public static final String[] sAvcodecOptimize_SkipLoopfilter_BiDir = new String[] { ":avcodec-skiploopfilter=2" }; public static final String[] sAvcodecOptimize_SkipLoopfilter_NonKey = new String[] { ":avcodec-skiploopfilter=3" }; public static final String[] sAvcodecOptimize_SkipLoopfilter_All = new String[] { ":avcodec-skiploopfilter=4" }; public static final String[] sAvcodecOptimize_SkipNone = new String[] {}; // membuf public static final String[] sMembuf_Enable = new String[] { ":membuf-enable=1" }; // audio stretch public static final String[] sAudioStretch_Enable = new String[] { ":audio-time-stretch" }; public static final String[] sHttpUserAgent_BiliDroid = new String[] { ":http-user-agent=" + Pragma.BILI_HTTP_UA_BILIDROID }; private LibVLC mLibVLC; private String[] mVlcPlayerOptions; private SurfaceHolder mSurfaceHolder; private LibVlcVideoSegmentListLoader mIndexResolver; private String mRawVideoMrl; private String mVideoMrl; private int mVideoHeight; private int mVideoWidth; private boolean mHasReadMedia; private boolean mPrepared; private float mLastBufferingPercent; private boolean mCompleted; private int mSeekWhenDurationChanged; private WeakReference<Context> mWeakContext; private WeakHandler mVlcHandler; private ModuleInfo mModuleInfo; public static LibVlcMediaPlayer createWithOptions(Context context, LibVlcLibraryLoader libLoader, PlayIndex.Resolver resolver, String[]... extraParams) throws LibVlcException { // options ArrayList<String> options = new ArrayList<String>(); CollectionHelper.Append(options, extraParams); return create(context, libLoader, resolver, CollectionHelper.toArray(options)); } private static LibVlcMediaPlayer create(Context context, LibVlcLibraryLoader libLoader, PlayIndex.Resolver resolver, String... extraParams) throws LibVlcException { DebugLog.v(TAG, "create"); LibVlcMediaPlayer player = new LibVlcMediaPlayer(context, resolver); try { player.mLibVLC = LibVLC.getInstance(context, libLoader); EventManager.getIntance().addHandler(player.mVlcHandler); // options ArrayList<String> options = new ArrayList<String>(); CollectionHelper.Append(options, extraParams); // TODO: add options here player.mVlcPlayerOptions = options.toArray(new String[options .size()]); return player; } catch (LibVlcException e) { e.printStackTrace(); } return null; } protected LibVlcMediaPlayer(Context context, PlayIndex.Resolver resolver) { mVlcHandler = new WeakHandler(this); mWeakContext = new WeakReference<Context>(context); mIndexResolver = new LibVlcVideoSegmentListLoader(context, resolver); } @Override public int getCurrentPosition() { return (int) mLibVLC.getTime(); } @Override public int getDuration() { int duration = (int) mLibVLC.getLength(); return duration; } @Override public int getVideoHeight() { return mVideoHeight; } @Override public int getVideoWidth() { return mVideoWidth; } @Override public boolean isPlaying() { return mLibVLC.isPlaying(); } @Override public void start() throws IllegalStateException { if (mHasReadMedia) { DebugLog.v(TAG, "play"); if (mCompleted) { restart(); } else { if (!mLibVLC.isPlaying()) { DebugLog.v(TAG, "start:play"); mLibVLC.play(); } } } else { DebugLog.v(TAG, "start:readMediaEX"); mLibVLC.readMediaEx(mVideoMrl, mVlcPlayerOptions); mHasReadMedia = true; } } private void restart() { DebugLog.v(TAG, "restart"); mCompleted = false; mLibVLC.stop(); mLibVLC.setPosition(0); mLibVLC.play(); } @Override public void stop() throws IllegalStateException { DebugLog.v(TAG, "stop"); mLibVLC.stop(); } @Override public void pause() throws IllegalStateException { DebugLog.v(TAG, "pause"); mLibVLC.pause(); } @Override public void prepareAsync() throws IllegalStateException { Assure.checkNotNull(mVideoMrl); DebugLog.v(TAG, "prepareAsync:readMediaEx"); mLibVLC.readMediaEx(mVideoMrl, mVlcPlayerOptions); mHasReadMedia = true; // Message msg = mSurfaceHandler.obtainMessage(MSG_FAKE_PREPARED); // mSurfaceHandler.sendMessage(msg); } @Override public void release() { DebugLog.v(TAG, "release"); mLibVLC.stop(); EventManager.getIntance().removeHandler(mVlcHandler); } @Override public void reset() { DebugLog.v(TAG, "reset"); mLibVLC.stop(); mPrepared = false; } @Override public void seekTo(int msec) throws IllegalStateException { int duration = getDuration(); if (duration <= 0) { DebugLog.e(TAG, "no duration for seek, try later"); mSeekWhenDurationChanged = msec; return; } DebugLog.v(TAG, "vlc seek to " + msec); mLibVLC.setTime(msec); mSeekWhenDurationChanged = 0; } @Override public void setDataSource(String uri) throws IOException, IllegalArgumentException, IllegalStateException { DebugLog.v(TAG, "vlc play " + uri); mRawVideoMrl = uri; // mRawVideoMrl = "http:/sina-hlv/v.iask.com/v_play.php?vid=47424256"; // mRawVideoMrl = // "http:/youku-750000/v.youku.com/player/getPlayList/VideoIDS/XNDQ3MDc2MjYw"; // mRawVideoMrl = // "http:/youku-2000000/v.youku.com/player/getPlayList/VideoIDS/XNDQ2MDA0MDYw"; // mRawVideoMrl = "http:/sina-hlv/v.iask.com/v_play.php?vid=78817175"; /*- * http://v.youku.com/player/getPlayList/VideoIDS/XNDQ2MDA0MDYw * http://v.iask.com/v_play.php?vid=78817175 * http://hot.vrs.sohu.com/vrs_flash.action?vid=717069 * http://cache.video.qiyi.com/v/6fb817df2a5c4e9fac8fa9e6af5c5935 * http://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid=9f2f1704cae745cb99be4ceb207d0f97 */ mIndexResolver.parseIndexMrl(mRawVideoMrl); mVideoMrl = mIndexResolver.getIndexMrlForVlcPlayer(); } @SuppressWarnings("deprecation") @Override public void setDisplay(SurfaceHolder holder) { DebugLog.v(TAG, "setDisplay"); mSurfaceHolder = holder; if (holder != null) { holder.addCallback(mSurfaceCallback); holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL); holder.setFormat(PixelFormat.RGBX_8888); } else { mLibVLC.detachSurface(); } } private SurfaceHolder.Callback mSurfaceCallback = new Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mLibVLC.attachSurface(holder.getSurface(), LibVlcMediaPlayer.this, width, height); } public void surfaceCreated(SurfaceHolder holder) { } public void surfaceDestroyed(SurfaceHolder holder) { mLibVLC.detachSurface(); } }; @Override public void setAudioStreamType(int streamMusic) { // TODO:implement } @Override public void setScreenOnWhilePlaying(boolean screenOn) { if (mSurfaceHolder != null) { mSurfaceHolder.setKeepScreenOn(screenOn); } } @Override public void enableLog(boolean enable) { if (mLibVLC != null) mLibVLC.changeVerbosity(enable); } @Override public boolean isBufferingEnd() { return true; } @Override public ModuleInfo getModuleInfo() { return mModuleInfo; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case LibVlcMessages.VLC_VIDEO_SIZE_CHANGED: if (mOnVideoSizeChangedListener != null) { mOnVideoSizeChangedListener.onVideoSizeChanged( LibVlcMediaPlayer.this, mVideoWidth, mVideoHeight); } return true; case LibVlcMessages.VLC_FAKE_PREPARED: if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(LibVlcMediaPlayer.this); } return true; case LibVlcMessages.VLC_EVENT: // process later break; default: return false; } switch (msg.arg1) { case EventManager.MediaPlayerLengthChanged: case EventManager.MediaDurationChanged: { DebugLog.v(TAG, String.format("vlc duration changed %d", getDuration())); if (mSeekWhenDurationChanged != 0) { seekTo(mSeekWhenDurationChanged); } break; } case EventManager.MediaStateChanged: { break; } case EventManager.MediaPlayerOpening: { break; } case EventManager.MediaPlayerBuffering: { if (mOnInfoListener != null) { if (msg.obj != null) { float percent = MediaPlayerBuffering .getNewCache((Bundle) msg.obj); // 这里返回的是缓冲百分比,而不是缓冲位置 if (percent < mLastBufferingPercent) { if (mOnInfoListener != null) { mOnInfoListener.onInfo(this, MediaPlayer.MEDIA_INFO_BUFFERING_START, 0); } } if (percent >= 100.0f) { /* * if (mOnInfoListener != null) { * mOnInfoListener.onInfo(LibVlcMediaPlayer.this, * PlayerAdapter.MEDIA_INFO_BUFFERING_END, 0); } */ if (!mPrepared) { mPrepared = true; if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(this); } } } mLastBufferingPercent = percent; } } break; } case EventManager.MediaPlayerBufferingTotal: { if (mOnBufferingUpdateListener != null) { if (msg.obj != null) { float percent = MediaPlayerBufferingTotal .getNewCacheTotal((Bundle) msg.obj); // 这里返回的是缓冲比例,而不是缓冲位置 DebugLog.v(TAG, "buffering " + percent); mOnBufferingUpdateListener.onBufferingUpdate(this, (int) percent); } } break; } case EventManager.MediaPlayerPlaying: { break; } case EventManager.MediaPlayerPaused: { break; } case EventManager.MediaPlayerStopped: { break; } case EventManager.MediaPlayerEndReached: { mCompleted = true; if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(this); } break; } case EventManager.MediaPlayerEncounteredError: { if (mOnErrorListener != null) { mOnErrorListener.onError(this, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); } break; } case EventManager.MediaPlayerSeekableChanged: { if (mOnInfoListener != null) { if (msg.obj != null) { boolean seekable = MediaPlayerSeekableChanged .getNewSeekable((Bundle) msg.obj); if (!seekable && mOnInfoListener != null) { mOnInfoListener.onInfo(this, MediaPlayer.MEDIA_INFO_NOT_SEEKABLE, 0); } } } break; } case EventManager.MediaPlayerPausableChanged: { break; } case EventManager.MediaPlayerModuleChanged: { if (msg.obj != null) { mModuleInfo = ModuleInfo.parseModuleInfo((Bundle) msg.obj); } break; } default: return false; } return true; }; /*--------------------------------- * getter */ @SuppressWarnings("unused") private Context getContext() { return mWeakContext.get(); } /*--------------------------------- * call from native */ public void setSurfaceSize(int width, int height) { DebugLog.vfmt(TAG, "native setSurfaceSize %d, %d", width, height); mVideoWidth = width; mVideoHeight = height; Message msg = mVlcHandler .obtainMessage(LibVlcMessages.VLC_VIDEO_SIZE_CHANGED); mVlcHandler.sendMessage(msg); } public boolean vslLoad(boolean forceReload) { return mIndexResolver.loadIndex(forceReload); } public Bundle vslGetBundle() { return mIndexResolver.getIndexBundle(); } }