1. 程式人生 > >AcFunDanmakuParser與DanmakuFlameMaster自定義解析器

AcFunDanmakuParser與DanmakuFlameMaster自定義解析器

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的實體類