package me.qixingchen.mdbilibili.ui.activity;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.controller.IDanmakuView;
import master.flame.danmaku.danmaku.loader.ILoader;
import master.flame.danmaku.danmaku.loader.IllegalDataException;
import master.flame.danmaku.danmaku.loader.android.DanmakuLoaderFactory;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.android.DanmakuContext;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.danmaku.parser.IDataSource;
import master.flame.danmaku.danmaku.parser.android.BiliDanmukuParser;
import me.qixingchen.mdbilibili.R;
import me.qixingchen.mdbilibili.app.Secret;
import me.qixingchen.mdbilibili.model.VideoHDM;
import me.qixingchen.mdbilibili.model.VideoM;
import me.qixingchen.mdbilibili.network.Api;
import me.qixingchen.mdbilibili.network.DownloadXML;
import me.qixingchen.mdbilibili.network.RetrofitNetwork;
import me.qixingchen.mdbilibili.ui.base.BaseActivity;
import me.qixingchen.mdbilibili.widget.MediaController;
import me.qixingchen.mdbilibili.widget.VideoView;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import tv.danmaku.ijk.media.player.IMediaPlayer;
public class PlayerActivity extends BaseActivity {
private static final String TAG = PlayerActivity.class.getSimpleName();
private static BaseDanmakuParser mDanmakuParser;
public Activity mActivity;
private String mXMLFileName;
private VideoView mPlayerView;
private IDanmakuView mDanmakuView;
private View mBufferingIndicator;
private MediaController mMediaController;
//页面切换时,播放到的位置
private int LastPosition = 0;
//VideoView OnInfo 当正在缓冲时等事件会被调用
// todo 显示缓冲提示
private IMediaPlayer.OnInfoListener onInfoListener = new IMediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(IMediaPlayer mp, int what, int extra) {
if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_START) {
if (mDanmakuView != null && mDanmakuView.isPrepared()) {
mDanmakuView.pause();
if (mBufferingIndicator != null)
mBufferingIndicator.setVisibility(View.VISIBLE);
}
} else if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_END) {
if (mDanmakuView != null && mDanmakuView.isPaused()) {
mDanmakuView.resume();
}
if (mBufferingIndicator != null)
mBufferingIndicator.setVisibility(View.GONE);
}
return true;
}
};
//跳转
private IMediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(IMediaPlayer mp) {
if (mDanmakuView != null && mDanmakuView.isPrepared()) {
mDanmakuView.seekTo(mp.getCurrentPosition());
}
}
};
//播放完成
private IMediaPlayer.OnCompletionListener onCompletionListener = new IMediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(IMediaPlayer mp) {
if (mDanmakuView != null && mDanmakuView.isPrepared()) {
mDanmakuView.seekTo((long) 0);
mDanmakuView.pause();
}
mPlayerView.pause();
}
};
//被控制条控制状态
private VideoView.OnControllerEventsListener onControllerEventsListener = new VideoView.OnControllerEventsListener() {
@Override
public void onVideoPause() {
if (mDanmakuView != null && mDanmakuView.isPrepared()) {
mDanmakuView.pause();
}
}
@Override
public void OnVideoResume() {
if (mDanmakuView != null && mDanmakuView.isPaused()) {
mDanmakuView.resume();
}
}
};
//弹幕加载 传入文件流
private static BaseDanmakuParser createParser(InputStream stream) {
if (stream == null) {
return new BaseDanmakuParser() {
@Override
protected Danmakus parse() {
return new Danmakus();
}
};
}
ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI);
try {
loader.load(stream);
} catch (IllegalDataException e) {
e.printStackTrace();
}
BaseDanmakuParser parser = new BiliDanmukuParser();
IDataSource<?> dataSource = loader.getDataSource();
parser.load(dataSource);
return parser;
}
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
@Override
protected int getContentView() {
return R.layout.activity_player;
}
@Override
protected void bindView() {
mDanmakuView = (IDanmakuView) findViewById(R.id.sv_danmaku);
mPlayerView = (VideoView) findViewById(R.id.playerView);
mBufferingIndicator = findViewById(R.id.buffering_indicator);
}
@Override
protected void initData() {
mMediaController = new MediaController(this);
mActivity = this;
mDanmakuView.enableDanmakuDrawingCache(true);
Intent intent = getIntent();
int aid = intent.getIntExtra("AID", 0);
mPlayerView.setMediaController(mMediaController);
mPlayerView.setMediaBufferingIndicator(mBufferingIndicator);
mPlayerView.requestFocus();
if (mBufferingIndicator != null)
mBufferingIndicator.setVisibility(View.VISIBLE);
Observable<VideoM> observable = RetrofitNetwork.retrofitMain.create(Api.VideoApi.class).getVideoApiRx(aid)
.subscribeOn(Schedulers.io());
//danmaku
Observable<Object> danmakuObservable =
observable
.map(new Func1<VideoM, String>() {
@Override
public String call(VideoM videoM) {
if (videoM.getCid() == null || videoM.getCid().contentEquals("undefined")) {
return "error";
}
mXMLFileName = videoM.getCid().substring(videoM.getCid().lastIndexOf('/') + 1);
String CID = mXMLFileName.substring(0, mXMLFileName.lastIndexOf("."));
return "http://www.bilibilijj.com/ashx/Barrage" +
".ashx?f=true&av=&p=&s=xml&cid=" + CID + "&n=" + CID;
}
})
.flatMap(new Func1<String, Observable<File>>() {
@Override
public Observable<File> call(String string) {
if (string.equals("error")) {
return Observable.error(new Exception("视频不存在或不能播放"));
}
return new DownloadXML().downloadXML(string);
}
})
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<File, Observable<?>>() {
@Override
public Observable<?> call(File file) {
return preparDanmaku(file);
}
});
//video
Observable<Object> videoObservable =
observable
.map(new Func1<VideoM, String>() {
@Override
public String call(VideoM videoM) {
return videoM.getCid();
}
})
.map(new Func1<String, String>() {
@Override
public String call(String s) {
if (s == null || s.contentEquals("undefined")) {
return "error";
}
return s.substring(s.lastIndexOf('/') + 1, s.lastIndexOf("."));
}
})
.flatMap(new Func1<String, Observable<VideoHDM>>() {
@Override
public Observable<VideoHDM> call(String cid) {
if (cid.equals("error")) {
return Observable.error(new Exception("视频不存在或不能播放"));
}
return RetrofitNetwork.getRetrofitNoBaseUri().baseUrl("http://interface.bilibili.com/")
.build().create(Api.VideoApi.class).getVideoApi("json", cid, "mp4", 4, Secret.App_Key);
}
})
.map(new Func1<VideoHDM, Uri>() {
@Override
public Uri call(VideoHDM videoHDM) {
return Uri.parse(videoHDM.getDurl().get(0).getUrl());
}
})
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<Uri, Observable<?>>() {
@Override
public Observable<?> call(Uri uri) {
return preparVideo(uri);
}
});
Observable
.merge(videoObservable, danmakuObservable)
.last()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
@Override
public void onCompleted() {
mPlayerView.start();
mDanmakuView.start();
}
@Override
public void onError(Throwable e) {
Snackbar.make(view, e.getMessage(), Snackbar.LENGTH_LONG).show();
e.printStackTrace();
}
@Override
public void onNext(Object o) {
}
});
}
@Override
protected void bindEvent() {
mPlayerView.setOnInfoListener(onInfoListener);
mPlayerView.setOnSeekCompleteListener(onSeekCompleteListener);
mPlayerView.setOnCompletionListener(onCompletionListener);
mPlayerView.setOnControllerEventsListener(onControllerEventsListener);
}
@Override
protected void onResume() {
super.onResume();
if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {
mDanmakuView.seekTo((long) LastPosition);
}
//todo 看看能不能保留缓冲的视频
if (mPlayerView != null && !mPlayerView.isPlaying()) {
mPlayerView.seekTo(LastPosition);
}
}
@Override
protected void onPause() {
super.onPause();
if (mPlayerView != null) {
LastPosition = mPlayerView.getCurrentPosition();
mPlayerView.pause();
}
if (mDanmakuView != null && mDanmakuView.isPrepared()) {
mDanmakuView.pause();
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
if (mDanmakuView != null) {
// dont forget release!
mDanmakuView.release();
mDanmakuView = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPlayerView != null && mPlayerView.isDrawingCacheEnabled()) {
mPlayerView.destroyDrawingCache();
}
if (mDanmakuView != null && mDanmakuView.isPaused()) {
mDanmakuView.release();
}
}
//TODO 播放器解码失败时重试
//TODO 修改代码结构,重写文件下载
//TODO 错误提示
//视频加载
public Observable preparVideo(final Uri src) {
return Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(final Subscriber<? super Object> subscriber) {
mPlayerView.setVideoURI(src);
mPlayerView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(IMediaPlayer mp) {
subscriber.onCompleted();
if (mBufferingIndicator != null)
mBufferingIndicator.setVisibility(View.GONE);
}
});
}
});
}
//danmaku加载
public Observable preparDanmaku(final File xmlFile) {
return Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(final Subscriber<? super Object> subscriber) {
try {
InputStream inputStream = new FileInputStream(xmlFile);
mDanmakuParser = createParser(inputStream);
mDanmakuView.setCallback(new DrawHandler.Callback() {
@Override
public void prepared() {
subscriber.onCompleted();
}
@Override
public void updateTimer(DanmakuTimer danmakuTimer) {
}
@Override
public void danmakuShown(BaseDanmaku danmaku) {
}
@Override
public void drawingFinished() {
}
});
mDanmakuView.prepare(mDanmakuParser, new DanmakuContext());
} catch (FileNotFoundException e) {
e.printStackTrace();
subscriber.onError(e);
}
}
});
}
}