/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.media.flv.amf; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.util.BufferUtil; /** * amf3のデータを扱うクラス * @author taktod */ public class Amf3Value { /** * データタイプ */ public enum Type { Undefined(0x00), Null(0x01), False(0x02), True(0x03), Integer(0x04), Double(0x05), String(0x06), XmlDoc(0x07), Date(0x08), Array(0x09), Object(0x0A), Xml(0x0B), ByteArray(0x0C), VectorInt(0x0D), VectorUint(0x0E), VectorDouble(0x0F), VectorObject(0x10), Dictionary(0x11); private final int value; private Type(int value) { this.value = value; } public int intValue() { return value; } public static Type getType(int value) { for(Type t : values()) { if(t.intValue() == value) { return t; } } throw new RuntimeException("解析不能なデータでした。" + value); } } /** * ファイルからデータを呼び出してオブジェクト化していく * @param source * @return * @throws Exception */ public static Object getValueObject(IReadChannel source) throws Exception { ByteBuffer data = null; // 先頭のデータを読み込みます。 Type type = Type.getType(BufferUtil.safeRead(source, 1).get()); switch(type) { case Null: return null; case False: return false; case True: return true; case Integer: return getU29Integer(source); case Double: return Double.longBitsToDouble(BufferUtil.safeRead(source, 8).getLong()); case Object: { // 次のバイトは0x0Bであることを期待します。しらないデータがでた場合は、あとで解析してプログラムを合わせる予定 data = BufferUtil.safeRead(source, 2); if(data.get() != 0x0B) { throw new Exception("知らないコードがきました。"); } if(data.get() != 0x01) { throw new Exception("objectの始点がおかしいです。"); } Map<String, Object> result = new HashMap<String, Object>(); // dataを読み込む byte b; while((b = BufferUtil.safeRead(source, 1).get()) != 0x01) { if((b & 0x01) == 0) { throw new Exception("リファレンスベースの動作はまだ作成していません。"); } data = BufferUtil.safeRead(source, b >>> 1); String key = new String(data.array()).intern(); // AMF3のデータではいっているので、取得する。 result.put(key, getValueObject(source)); } return result; } case String: { byte b = BufferUtil.safeRead(source, 1).get(); if((b & 0x01) != 0x01) { throw new RuntimeException("参照値は未定義"); } data = BufferUtil.safeRead(source, b >>> 1); return new String(data.array()).intern(); } case Undefined: break; default: throw new Exception("解析不能なデータ:" + type); } // データを解析していく return null; } /** * U29のデータを解析します * @param source * @return * @throws Exception */ public static int getU29Integer(IReadChannel source) throws Exception { byte b = BufferUtil.safeRead(source, 1).get(); int data = 0; while((b & 0x80) != 0x00) { data = (data << 7) + (b & 0x7F); b = BufferUtil.safeRead(source, 1).get(); } data = (data << 7) + (b & 0x7F); return data; } } /* * 11 0A 0B 01(クラス名:なし) * 03(参照なしの文字列1) 63(A) * 0A(object) 01(クラス名なし?) [03 41 文字Aのキー] [04 01(int 1)] 01 03 62 06 05 31 34 03 61 04 0D 01 * * 00 02 00 0A 6F 6E 4D 65 74 61 44 61 74 61 * 11 0A 0B 01 03 63 * 0A 03 01 03 62 06 05 31 34 03 61 04 0D 01 */