AcFunDanmakuParser与DanmakuFlameMaster自定义解析器

越嘉茂
2023-12-01

bilibili删掉了DanmakuFlameMaster源码里的AcFunDanmakuParser,之前官方(包括目前网上各种教程里的AcFunDanmakuParser)也已经不适用,而官方给我们的BiliDanmukuParser不支持json,因此我重写了一个支持json的AcFunDanmakuParser,支持的json格式为A站默认的json格式

[{"c":"0,16777215,1,25,196050,1364468342","m":"弹幕弹幕弹幕"},{"c":"3.619,16777215,1,25,196050,1364468347","m":"弹幕弹幕弹幕弹幕弹幕弹幕"}]

参数:{"c": "播放时间,颜色,模式,字号,uid,发送时间", "m": "弹幕内容"} 
其中uid和发送时间可以自定义,由自己判断是否需要改变弹幕样式
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.graphics.Color;


import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.danmaku.parser.android.JSONSource;

public class AcFunDanmakuParser extends BaseDanmakuParser {

    @Override
    public Danmakus parse() {
        if (mDataSource != null && mDataSource instanceof JSONSource) {
            JSONSource jsonSource = (JSONSource) mDataSource;
            return doParse(jsonSource.data());
        }
        return new Danmakus();
    }

    /**
     * @param danmakuListData 弹幕数据
     *                        传入的数组内包含普通弹幕,会员弹幕,锁定弹幕。
     * @return 转换后的Danmakus
     */
    private Danmakus doParse(JSONArray danmakuListData) {
        Danmakus danmakus = new Danmakus();
        if (danmakuListData == null || danmakuListData.length() == 0) {
            return danmakus;
        }
        danmakus = _parse(danmakuListData, danmakus);
        return danmakus;
    }

    private Danmakus _parse(JSONArray jsonArray, Danmakus danmakus) {
        if (danmakus == null) {
            danmakus = new Danmakus();
        }
        if (jsonArray == null || jsonArray.length() == 0) {
            return danmakus;
        }
        for (int i = 0; i < jsonArray.length(); i++) {
            try {
                JSONObject obj = jsonArray.getJSONObject(i);
                String c = obj.getString("c");
                String[] values = c.split(",");
                if (values.length > 0) {
                    int type = Integer.parseInt(values[2]); // 弹幕类型
                    if (type == 7)
                        // FIXME : hard code
                        // TODO : parse advance danmaku json
                        continue;
                    long time = (long) (Float.parseFloat(values[0]) * 1000); // 出现时间
                    int color = Integer.parseInt(values[1]) | 0xFF000000; // 颜色
                    float textSize = Float.parseFloat(values[3]); // 字体大小
                    BaseDanmaku item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
                    if (item != null) {
                        item.setTime(time);
                        item.textSize = textSize * (mDispDensity - 0.6f);
                        item.textColor = color;
                        item.textShadowColor = color <= Color.BLACK ? Color.WHITE : Color.BLACK;
                        item.index = i;
                        item.flags = mContext.mGlobalFlagValues;
                        item.setTimer(mTimer);
                        item.text = obj.getString("m");
                        danmakus.addItem(item);
                    }
                }
            } catch (JSONException e) {
            } catch (NumberFormatException e) {
            }
        }
        return danmakus;
    }
}

使用的时候可以这样用(参考官方的demo):

private BaseDanmakuParser createParser(InputStream stream) {
        if (stream == null) {
            return new BaseDanmakuParser() {

                @Override
                protected Danmakus parse() {
                    return new Danmakus();
                }
            };
        }

        ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_ACFUN);

        try {
            loader.load(stream);
        } catch (IllegalDataException e) {
            e.printStackTrace();
        }
        BaseDanmakuParser parser = new AcFunDanmakuParser();
        IDataSource<?> dataSource = loader.getDataSource();
        parser.load(dataSource);
        return parser;

    }

其实也不难理解,从JSONArray里解析出弹幕的内容,颜色,时间,颜色等属性,然后填充进BaseDanmaku里面,再把BaseDanmaku添加进Danmakus里,最后把Danmakus返回回去就行,JSONArray相信大家都懂得,知道这点之后,其实我们也没必要严格遵循官方的格式了。

甚至我们可以重写BaseDanmakuParser、ILoader、IDataSource这三个类,来实现我们自己的Parser,这里我写一个JsonParser供大家参考

import android.graphics.Color;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.videoplayertest.bean.DanmakuBean;

import java.io.Reader;
import java.util.List;

import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;

public class JsonParser extends BaseDanmakuParser {
    @Override
    protected Danmakus parse() {
        Danmakus danmakus = new Danmakus();
        if(mDataSource != null && mDataSource instanceof JsonSource){
            JsonSource source = (JsonSource) this.mDataSource;
            return doParse(source.data());
        }
        return danmakus;
    }

    private Danmakus doParse(Reader json) {
        Danmakus danmakus = new Danmakus();
        //在这里解析json,填充进BaseDanmaku里
        Gson gson = new Gson();
        List<DanmakuBean> beans = gson.fromJson(json, new TypeToken<List<DanmakuBean>>() {
        }.getType());

        if(beans != null && beans.size()>0){
            int size = beans.size();
            for (int i = 0 ; i < size ; i++){
                DanmakuBean bean = beans.get(i);
                int type = bean.getType();
                if (type == 7)
                    continue;
                BaseDanmaku item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
                item.flags = mContext.mGlobalFlagValues;
                item.textSize = bean.getTextSize() * (mDispDensity - 0.6f);
                item.textColor = bean.getTextColor();
                item.textShadowColor = bean.getTextColor() <= Color.BLACK ? Color.WHITE : Color.BLACK;
                item.setTime((long) (bean.getTime()*1000));
                item.setTimer(mTimer);
                item.text = bean.getText();
                item.index = i;
                danmakus.addItem(item);
            }

        }
        return danmakus;
    }
}
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;

import master.flame.danmaku.danmaku.loader.ILoader;
import master.flame.danmaku.danmaku.loader.IllegalDataException;

public class JsonLoader implements ILoader {

    private static volatile JsonLoader instance;
    private JsonSource source;

    public static ILoader instance() {
        if(instance == null){
            synchronized (JsonLoader.class){
                if(instance == null)
                    instance = new JsonLoader();
            }
        }
        return instance;
    }


    @Override
    public JsonSource getDataSource() {
        return source;
    }

    @Override
    public void load(String uri) throws IllegalDataException {
        load(new StringReader(uri));
    }

    @Override
    public void load(InputStream in) throws IllegalDataException {
        InputStreamReader reader = new InputStreamReader(in);
        load(reader);
    }

    public void load(Reader reader) throws IllegalDataException {
        source = new JsonSource(reader);
    }
}

 

import java.io.Reader;
import master.flame.danmaku.danmaku.parser.IDataSource;

/**
 * 考虑到弹幕数据可能较多,为避免String溢出,这里建议使用reader代替String
 * Gson可以直接使用reader,而且Reader方便在String和InputStream之间转换
 */
public class JsonSource implements IDataSource<Reader> {
    Reader reader;

    public JsonSource(Reader reader) {
        this.reader = reader;
    }

    @Override
    public Reader data() {
        return reader;
    }

    @Override
    public void release() {
        reader = null;
    }
}

使用的时候把InputStream 传进JsonLoader即可

   private BaseDanmakuParser createParser(InputStream stream) {
        ILoader loader = JsonLoader.instance();
        try {
            loader.load(stream);
        } catch (IllegalDataException e) {
            e.printStackTrace();
        }
        JsonParser parser = new JsonParser();
        IDataSource<?> dataSource = loader.getDataSource();
        parser.load(dataSource);
        return parser;

    }

我自定义的json格式为:

[{"millis":0,"text":"弹幕111111","textColor":-65536,"textSize":20.0,"time":0.6,"type":1,"userId":0},{"millis":0,"text":"弹幕111111","textColor":-16776961,"textSize":20.0,"time":0.6,"type":1,"userId":0}]

DanmakuBean为该json的实体类

 

 类似资料: