Support danmu - part 3

pull/142/head
FongMi 3 years ago
parent 2a131a0ecd
commit 5ccf98cdff
  1. 12
      app/src/main/java/com/fongmi/android/tv/bean/Result.java
  2. 2
      app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java
  3. 168
      app/src/main/java/com/fongmi/android/tv/player/danmu/Parser.java
  4. 7
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java
  5. 2
      danmaku/src/main/java/master/flame/danmaku/controller/DrawTask.java
  6. 1
      danmaku/src/main/java/master/flame/danmaku/danmaku/model/IDanmakus.java
  7. 1
      danmaku/src/main/java/master/flame/danmaku/danmaku/model/android/DanmakuFactory.java
  8. 4
      danmaku/src/main/java/master/flame/danmaku/danmaku/parser/BaseDanmakuParser.java
  9. 5
      danmaku/src/main/java/master/flame/danmaku/danmaku/util/DanmakuUtils.java
  10. 342
      danmaku/src/main/java/master/flame/danmaku/ui/widget/FakeDanmakuView.java

@ -32,6 +32,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import master.flame.danmaku.danmaku.model.IDanmakus;
@Root(name = "rss", strict = false)
public class Result implements Parcelable {
@ -79,7 +81,7 @@ public class Result implements Parcelable {
@SerializedName("msg")
private String msg;
private Danmu danmu;
private IDanmakus danmakus;
public static Result objectFrom(String str) {
try {
@ -251,12 +253,12 @@ public class Result implements Parcelable {
this.msg = msg;
}
public Danmu getDanmu() {
return danmu == null ? new Danmu() : danmu;
public IDanmakus getDanmakus() {
return danmakus;
}
public void setDanmu(Danmu danmu) {
this.danmu = danmu;
public void setDanmakus(IDanmakus danmakus) {
this.danmakus = danmakus;
}
public boolean hasMsg() {

@ -274,7 +274,7 @@ public class SiteViewModel extends ViewModel {
private void checkDanmaku(Result result) throws Exception {
if (result.getDanmaku().isEmpty()) return;
String body = OkHttp.newCall(result.getDanmaku()).execute().body().string();
result.setDanmu(Danmu.fromXml(body));
//result.setDanmakus(new Parser(body).getDanmakus());
SpiderDebug.log(body);
}

@ -1,20 +1,34 @@
package com.fongmi.android.tv.player.danmu;
import android.graphics.Color;
import android.text.TextUtils;
import com.fongmi.android.tv.bean.Danmu;
import org.json.JSONArray;
import org.json.JSONException;
import master.flame.danmaku.danmaku.model.AlphaValue;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.Duration;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.IDisplayer;
import master.flame.danmaku.danmaku.model.SpecialDanmaku;
import master.flame.danmaku.danmaku.model.android.DanmakuFactory;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.danmaku.util.DanmakuUtils;
public class Parser extends BaseDanmakuParser {
private final Danmu danmu;
private BaseDanmaku item;
private float scaleX;
private float scaleY;
private int index;
public Parser(Danmu danmu) {
this.danmu = danmu;
public Parser(String xml) {
this.danmu = Danmu.fromXml(xml);
}
@Override
@ -22,23 +36,145 @@ public class Parser extends BaseDanmakuParser {
Danmakus result = new Danmakus(IDanmakus.ST_BY_TIME);
for (Danmu.D d : danmu.getD()) {
String[] values = d.getP().split(",");
if (values.length < 8) continue;
int type = Integer.parseInt(values[1]);
long time = (long) (Float.parseFloat(values[0]) * 1000);
float size = Float.parseFloat(values[2]) * (mDispDensity - 0.6f);
int color = (int) ((0x00000000ff000000L | Long.parseLong(values[3])) & 0x00000000ffffffffL);
BaseDanmaku item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
item.setTime(time);
item.setTimer(mTimer);
item.setTextSize(size);
item.setTextColor(color);
item.setTextShadowColor(color <= Color.BLACK ? Color.WHITE : Color.BLACK);
item.setFlags(mContext.mGlobalFlagValues);
Object lock = result.obtainSynchronizer();
synchronized (lock) {
if (values.length < 4) continue;
setBase(values).setText(d);
synchronized (result.obtainSynchronizer()) {
result.addItem(item);
}
}
return result;
}
@Override
public BaseDanmakuParser setDisplay(IDisplayer display) {
super.setDisplay(display);
scaleX = mDispWidth / DanmakuFactory.BILI_PLAYER_WIDTH;
scaleY = mDispHeight / DanmakuFactory.BILI_PLAYER_HEIGHT;
return this;
}
private Parser setBase(String[] values) {
int type = Integer.parseInt(values[1]);
long time = (long) (Float.parseFloat(values[0]) * 1000);
float size = Float.parseFloat(values[2]) * (mDispDensity - 0.6f);
int color = (int) ((0x00000000ff000000L | Long.parseLong(values[3])) & 0x00000000ffffffffL);
item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
item.setTime(time);
item.setTimer(mTimer);
item.setTextSize(size);
item.setTextColor(color);
item.setTextShadowColor(color <= Color.BLACK ? Color.WHITE : Color.BLACK);
item.setFlags(mContext.mGlobalFlagValues);
return this;
}
private void setText(Danmu.D d) {
item.index = index++;
DanmakuUtils.fillText(item, decodeXmlString(d.getT()));
String text = item.text.toString().trim();
if (item.getType() == BaseDanmaku.TYPE_SPECIAL && text.startsWith("[") && text.endsWith("]")) setSpecial(text);
}
private void setSpecial(String text) {
String[] textArr = null;
try {
JSONArray jsonArray = new JSONArray(text);
textArr = new String[jsonArray.length()];
for (int i = 0; i < textArr.length; i++) {
textArr[i] = jsonArray.getString(i);
}
} catch (JSONException e) {
e.printStackTrace();
}
if (textArr == null || textArr.length < 5 || TextUtils.isEmpty(textArr[4])) {
item = null;
return;
}
DanmakuUtils.fillText(item, textArr[4]);
float beginX = Float.parseFloat(textArr[0]);
float beginY = Float.parseFloat(textArr[1]);
float endX = beginX;
float endY = beginY;
String[] alphaArr = textArr[2].split("-");
int beginAlpha = (int) (AlphaValue.MAX * Float.parseFloat(alphaArr[0]));
int endAlpha = beginAlpha;
if (alphaArr.length > 1) {
endAlpha = (int) (AlphaValue.MAX * Float.parseFloat(alphaArr[1]));
}
long alphaDuraion = (long) (Float.parseFloat(textArr[3]) * 1000);
long translationDuration = alphaDuraion;
long translationStartDelay = 0;
float rotateY = 0, rotateZ = 0;
if (textArr.length >= 7) {
rotateZ = Float.parseFloat(textArr[5]);
rotateY = Float.parseFloat(textArr[6]);
}
if (textArr.length >= 11) {
endX = Float.parseFloat(textArr[7]);
endY = Float.parseFloat(textArr[8]);
if (!"".equals(textArr[9])) {
translationDuration = Integer.parseInt(textArr[9]);
}
if (!"".equals(textArr[10])) {
translationStartDelay = (long) (Float.parseFloat(textArr[10]));
}
}
if (isPercentageNumber(textArr[0])) {
beginX *= DanmakuFactory.BILI_PLAYER_WIDTH;
}
if (isPercentageNumber(textArr[1])) {
beginY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
}
if (textArr.length >= 8 && isPercentageNumber(textArr[7])) {
endX *= DanmakuFactory.BILI_PLAYER_WIDTH;
}
if (textArr.length >= 9 && isPercentageNumber(textArr[8])) {
endY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
}
item.duration = new Duration(alphaDuraion);
item.rotationZ = rotateZ;
item.rotationY = rotateY;
mContext.mDanmakuFactory.fillTranslationData(item, beginX, beginY, endX, endY, translationDuration, translationStartDelay, scaleX, scaleY);
mContext.mDanmakuFactory.fillAlphaData(item, beginAlpha, endAlpha, alphaDuraion);
if (textArr.length >= 12) {
if (!TextUtils.isEmpty(textArr[11]) && "true".equalsIgnoreCase(textArr[11])) {
item.textShadowColor = Color.TRANSPARENT;
}
}
if (textArr.length >= 14) {
((SpecialDanmaku) item).isQuadraticEaseOut = ("0".equals(textArr[13]));
}
if (textArr.length >= 15) {
if (!"".equals(textArr[14])) {
String motionPathString = textArr[14].substring(1);
if (!TextUtils.isEmpty(motionPathString)) {
String[] pointStrArray = motionPathString.split("L");
if (pointStrArray.length > 0) {
float[][] points = new float[pointStrArray.length][2];
for (int i = 0; i < pointStrArray.length; i++) {
String[] pointArray = pointStrArray[i].split(",");
if (pointArray.length >= 2) {
points[i][0] = Float.parseFloat(pointArray[0]);
points[i][1] = Float.parseFloat(pointArray[1]);
}
}
DanmakuFactory.fillLinePathData(item, points, scaleX, scaleY);
}
}
}
}
}
private boolean isPercentageNumber(String number) {
return number != null && number.contains(".");
}
private String decodeXmlString(String title) {
if (title.contains("&amp;")) title = title.replace("&amp;", "&");
if (title.contains("&quot;")) title = title.replace("&quot;", "\"");
if (title.contains("&gt;")) title = title.replace("&gt;", ">");
if (title.contains("&lt;")) title = title.replace("&lt;", "<");
return title;
}
}

@ -40,7 +40,6 @@ import com.fongmi.android.tv.Constant;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.Setting;
import com.fongmi.android.tv.api.ApiConfig;
import com.fongmi.android.tv.bean.Danmu;
import com.fongmi.android.tv.bean.Episode;
import com.fongmi.android.tv.bean.Flag;
import com.fongmi.android.tv.bean.History;
@ -102,6 +101,7 @@ import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import master.flame.danmaku.danmaku.model.IDanmakus;
import tv.danmaku.ijk.media.player.ui.IjkVideoView;
public class VideoActivity extends BaseActivity implements Clock.Callback, CustomKeyDownVod.Listener, CastDialog.Listener, TrackDialog.Listener, ControlDialog.Listener, FlagAdapter.OnClickListener, EpisodeAdapter.OnClickListener, QualityAdapter.OnClickListener, QuickAdapter.OnClickListener, ParseAdapter.OnClickListener, SubtitleCallback {
@ -509,11 +509,10 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
mBinding.quality.setVisibility(result.getUrl().isOnly() ? View.GONE : View.VISIBLE);
mBinding.swipeLayout.setRefreshing(false);
mQualityAdapter.addAll(result);
loadDanmu(result.getDanmu());
loadDanmu(result.getDanmakus());
}
private void loadDanmu(Danmu danmu) {
if (danmu.getD().isEmpty()) return;
private void loadDanmu(IDanmakus danmakus) {
}

@ -309,7 +309,7 @@ public class DrawTask implements IDrawTask {
}
protected void loadDanmakus(BaseDanmakuParser parser) {
danmakuList = parser.setConfig(mContext).setDisplayer(mDisp).setTimer(mTimer).setListener(new BaseDanmakuParser.Listener() {
danmakuList = parser.setConfig(mContext).setDisplay(mDisp).setTimer(mTimer).setListener(new BaseDanmakuParser.Listener() {
@Override
public void onDanmakuAdd(BaseDanmaku danmaku) {
if (mTaskListener != null) {

@ -159,5 +159,4 @@ public interface IDanmakus {
return Float.compare(obj2.getTop(), obj1.getTop());
}
}
}

@ -55,7 +55,6 @@ public class DanmakuFactory {
private DanmakuContext sLastConfig;
protected DanmakuFactory() {
}
public static DanmakuFactory create() {

@ -34,11 +34,11 @@ public abstract class BaseDanmakuParser {
protected Listener mListener;
private IDanmakus mDanmakus;
public IDisplayer getDisplayer() {
public IDisplayer getDisplay() {
return mDisp;
}
public BaseDanmakuParser setDisplayer(IDisplayer disp) {
public BaseDanmakuParser setDisplay(IDisplayer disp) {
mDisp = disp;
mDispWidth = disp.getWidth();
mDispHeight = disp.getHeight();

@ -160,10 +160,7 @@ public class DanmakuUtils {
public static void fillText(BaseDanmaku danmaku, CharSequence text) {
danmaku.text = text;
if (TextUtils.isEmpty(text) || !text.toString().contains(BaseDanmaku.DANMAKU_BR_CHAR)) {
return;
}
if (TextUtils.isEmpty(text) || !text.toString().contains(BaseDanmaku.DANMAKU_BR_CHAR)) return;
String[] lines = String.valueOf(danmaku.text).split(BaseDanmaku.DANMAKU_BR_CHAR, -1);
if (lines.length > 1) {
danmaku.lines = lines;

@ -1,342 +0,0 @@
package master.flame.danmaku.ui.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.controller.DrawHelper;
import master.flame.danmaku.danmaku.model.AlphaValue;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.Duration;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.IDisplayer;
import master.flame.danmaku.danmaku.model.SpecialDanmaku;
import master.flame.danmaku.danmaku.model.android.DanmakuContext;
import master.flame.danmaku.danmaku.model.android.DanmakuFactory;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.danmaku.util.DanmakuUtils;
/**
* Created by ch on 17/8/18.
*/
public class FakeDanmakuView extends DanmakuView implements DrawHandler.Callback {
private DanmakuTimer mTimer;
private boolean mIsRelease;
private OnFrameAvailableListener mOnFrameAvailableListener;
private int mWidth = 0;
private int mHeight = 0;
private float mScale = 1f;
private DanmakuTimer mOuterTimer;
private long mBeginTimeMills;
private long mFrameIntervalMills = 16L;
private long mEndTimeMills;
private Bitmap mBufferBitmap;
private Canvas mBufferCanvas;
private int mRetryCount = 0;
private long mExpectBeginMills = 0;
public FakeDanmakuView(Context context) {
super(context);
}
public FakeDanmakuView(Context context, int width, int height, float scale) {
super(context);
mWidth = width;
mHeight = height;
mScale = scale;
initBufferCanvas(width, height);
}
public void initBufferCanvas(int width, int height) {
mBufferBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mBufferCanvas = new Canvas(mBufferBitmap);
}
@Override
public long drawDanmakus() {
if (mIsRelease) {
return 0;
}
Canvas canvas = mBufferCanvas;
if (canvas == null) {
return 0;
}
Bitmap bufferBitmap = this.mBufferBitmap;
if (bufferBitmap == null || bufferBitmap.isRecycled()) {
return 0;
}
bufferBitmap.eraseColor(Color.TRANSPARENT);
if (mClearFlag) {
DrawHelper.clearCanvas(canvas);
mClearFlag = false;
} else {
if (handler != null) {
handler.draw(canvas);
}
}
OnFrameAvailableListener onFrameAvailableListener = this.mOnFrameAvailableListener;
if (onFrameAvailableListener != null) {
long curr = mOuterTimer.currMillisecond;
try {
if (curr >= mExpectBeginMills - mFrameIntervalMills) {
Bitmap bitmap;
boolean recycle = false;
if (mScale == 1f) {
bitmap = bufferBitmap;
} else {
bitmap = Bitmap.createScaledBitmap(bufferBitmap, (int) (mWidth * mScale), (int) (mHeight * mScale), true);
recycle = true;
}
onFrameAvailableListener.onFrameAvailable(curr, bitmap);
if (recycle) {
bitmap.recycle();
}
}
} catch (Exception e) {
release();
onFrameAvailableListener.onFailed(101, e.getMessage());
} finally {
if (curr >= mEndTimeMills) {
release();
if (mTimer != null) {
mTimer.update(mEndTimeMills);
}
onFrameAvailableListener.onFramesFinished(curr);
}
}
}
mRequestRender = false;
return 2; // 固定频率
}
@Override
public void release() {
mIsRelease = true;
super.release();
mBufferBitmap = null;
}
@Override
protected void onDraw(Canvas canvas) {
}
@Override
public boolean isShown() {
return true;
}
@Override
public boolean isViewReady() {
return true;
}
@Override
public int getViewWidth() {
return mWidth;
}
@Override
public int getViewHeight() {
return mHeight;
}
@Override
public void prepare(BaseDanmakuParser parser, DanmakuContext config) {
CustomParser newParser = new CustomParser(parser, mBeginTimeMills, mEndTimeMills);
DanmakuContext configCopy;
try {
configCopy = (DanmakuContext) config.clone();
configCopy.resetContext();
configCopy.transparency = AlphaValue.MAX;
configCopy.setDanmakuTransparency(config.transparency / (float) AlphaValue.MAX);
configCopy.mGlobalFlagValues.FILTER_RESET_FLAG = config.mGlobalFlagValues.FILTER_RESET_FLAG;
configCopy.setDanmakuSync(null);
configCopy.unregisterAllConfigChangedCallbacks();
configCopy.mGlobalFlagValues.updateAll();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
configCopy = config;
}
configCopy.updateMethod = 1;
if (mOnFrameAvailableListener != null) {
mOnFrameAvailableListener.onConfig(configCopy);
}
super.prepare(newParser, configCopy);
handler.setIdleSleep(false);
handler.enableNonBlockMode(true);
}
public void setTimeRange(final long beginMills, final long endMills) {
mExpectBeginMills = beginMills;
mBeginTimeMills = Math.max(0, beginMills - 30000L); // FIXME: 17/8/23 magic code 30000L
mEndTimeMills = endMills;
}
public void setOnFrameAvailableListener(OnFrameAvailableListener onFrameAvailableListener) {
mOnFrameAvailableListener = onFrameAvailableListener;
}
public void getFrameAtTime(final int frameRate) {
if (mRetryCount++ > 5) {
release();
if (mOnFrameAvailableListener != null) {
mOnFrameAvailableListener.onFailed(100, "not prepared");
}
return;
}
if (!isPrepared()) {
DrawHandler handler = this.handler;
if (handler == null) {
return;
}
handler.postDelayed(new Runnable() {
@Override
public void run() {
getFrameAtTime(frameRate);
}
}, 1000L);
return;
}
mFrameIntervalMills = 1000 / frameRate;
setCallback(this);
long beginMills = Math.max(0, mExpectBeginMills - getConfig().mDanmakuFactory.MAX_DANMAKU_DURATION * 3 / 2);
mOuterTimer = new DanmakuTimer(beginMills);
start(beginMills);
}
@Override
public void prepared() {
}
@Override
public void updateTimer(DanmakuTimer timer) {
mTimer = timer;
timer.update(mOuterTimer.currMillisecond);
mOuterTimer.add(mFrameIntervalMills);
timer.add(mFrameIntervalMills);
}
@Override
public void danmakuShown(BaseDanmaku danmaku) {
}
@Override
public void drawingFinished() {
}
public interface OnFrameAvailableListener {
void onConfig(DanmakuContext config);
void onFrameAvailable(long timeMills, Bitmap bitmap);
void onFramesFinished(long timeMills);
void onFailed(int errorCode, String msg);
}
private class CustomParser extends BaseDanmakuParser {
private final BaseDanmakuParser mBaseParser;
private final long stTime;
private final long edTime;
private float mDispScaleX, mDispScaleY;
private int mViewWidth;
public CustomParser(BaseDanmakuParser baseParser, long stTime, long edTime) {
this.mBaseParser = baseParser;
this.stTime = stTime;
this.edTime = edTime;
}
@Override
protected IDanmakus parse() {
final IDanmakus danmakus = new Danmakus();
IDanmakus subnew;
try {
subnew = this.mBaseParser.getDanmakus().subnew(this.stTime, this.edTime);
} catch (Exception e) {
subnew = this.mBaseParser.getDanmakus();
}
if (subnew == null) {
return danmakus;
}
subnew.forEach(new IDanmakus.Consumer<BaseDanmaku, Object>() {
@Override
public int accept(BaseDanmaku danmaku) {
long time = danmaku.getTime();
if (time < stTime) {
return IDanmakus.Consumer.ACTION_CONTINUE;
} else if (time > edTime) {
return IDanmakus.Consumer.ACTION_BREAK;
}
BaseDanmaku item = mContext.mDanmakuFactory.createDanmaku(danmaku.getType(), mContext);
if (item != null) {
item.setTime(danmaku.getTime());
DanmakuUtils.fillText(item, danmaku.text);
item.textSize = danmaku.textSize;
item.textColor = danmaku.textColor;
item.textShadowColor = danmaku.textShadowColor;
if (danmaku instanceof SpecialDanmaku) {
SpecialDanmaku sdanmaku = (SpecialDanmaku) danmaku;
item.index = danmaku.index;
item.duration = new Duration(sdanmaku.getDuration());
item.rotationZ = sdanmaku.rotateZ;
item.rotationY = sdanmaku.rotationY;
((SpecialDanmaku) item).isQuadraticEaseOut = sdanmaku.isQuadraticEaseOut;
mContext.mDanmakuFactory.fillTranslationData(item, sdanmaku.beginX,
sdanmaku.beginY, sdanmaku.endX, sdanmaku.endY, sdanmaku.translationDuration, sdanmaku.translationStartDelay, mDispScaleX, mDispScaleY);
mContext.mDanmakuFactory.fillAlphaData(item, sdanmaku.beginAlpha, sdanmaku.endAlpha, item.getDuration());
// mContext.mDanmakuFactory.fillLinePathData(item, points, mDispScaleX,
// mDispScaleY); // FIXME
return 0; // FIXME skip special danmakus
}
item.setTimer(mTimer);
item.mFilterParam = danmaku.mFilterParam;
item.filterResetFlag = danmaku.filterResetFlag;
item.flags = mContext.mGlobalFlagValues;
Object lock = danmakus.obtainSynchronizer();
synchronized (lock) {
danmakus.addItem(item);
}
}
return 0;
}
});
return danmakus;
}
@Override
public BaseDanmakuParser setDisplayer(IDisplayer disp) {
super.setDisplayer(disp);
if (mBaseParser == null || mBaseParser.getDisplayer() == null) {
return this;
}
mDispScaleX = mDispWidth / (float) mBaseParser.getDisplayer().getWidth();
mDispScaleY = mDispHeight / (float) mBaseParser.getDisplayer().getHeight();
if (mViewWidth <= 1) {
mViewWidth = disp.getWidth();
}
return this;
}
@Override
protected float getViewportSizeFactor() {
float scale = DanmakuFactory.COMMON_DANMAKU_DURATION * mViewWidth / DanmakuFactory.BILI_PLAYER_WIDTH;
float factor = 1.1f;
return mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * factor / scale;
}
}
}
Loading…
Cancel
Save