[Red5commits] [1639] most of the basic AMF3 stuff working fine now

jbauch luke at codegent.com
Wed Jan 24 11:12:55 EST 2007


most of the basic AMF3 stuff working fine now


Timestamp: 01/21/07 15:56:03 EST (3 days ago) 
Change: 1639 
Author: jbauch

Files (see diff or trac for details): 
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Input.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Output.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/AMF3.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/flv/impl/FLVReader.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/mock/Input.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/mock/Output.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/mp3/impl/MP3Reader.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/BaseInput.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/BaseOutput.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/Deserializer.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/Input.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/Output.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/RecordSet.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/object/Serializer.java
java/server/branches/joachim_amf3_integration/src/org/red5/server/net/remoting/RemotingClient.java
java/server/branches/joachim_amf3_integration/src/org/red5/server/net/rtmp/codec/SharedObjectSerializer.java


Trac: http://mirror1.cvsdude.com/trac/osflash/red5/changeset/1639

Index: /va/server/branches/joachim_amf3_integration/src/org/red5/server/net/rtmp/codec/SharedObjectSerializer.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/server/net/rtmp/codec/SharedObjectSerializer.java (revision 1406)
+++  (revision )
@@ -1,77 +1,0 @@
-package org.red5.server.net.rtmp.codec;
-
-/*
- * RED5 Open Source Flash Server - http://www.osflash.org/red5
- * 
- * Copyright (c) 2006 by respective authors (see below). All rights reserved.
- * 
- * This library is free software; you can redistribute it and/or modify it under the 
- * terms of the GNU Lesser General Public License as published by the Free Software 
- * Foundation; either version 2.1 of the License, or (at your option) any later 
- * version. 
- * 
- * This library is distributed in the hope that it will be useful, but WITHOUT ANY 
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public License along 
- * with this library; if not, write to the Free Software Foundation, Inc., 
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
- */
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.collections.BeanMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.red5.io.object.Output;
-import org.red5.io.object.Serializer;
-
-/**
- * Data serializer for shared objects.
- * 
- * @author The Red5 Project (red5 at osflash.org)
- * @author Joachim Bauch (jojo at struktur.de)
- */
-public class SharedObjectSerializer extends Serializer {
-
-	// Initialize Logging
-	protected static Log log = LogFactory.getLog(SharedObjectSerializer.class
-			.getName());
-
-	/**
-	 * Writes a map to the output.
-	 * 
-	 * @param out
-	 * 			output stream
-	 * @param map
-	 * 			Map object to serialize
-	 */
-	@Override
-	public void writeMap(Output out, Map map) {
-		if (log.isDebugEnabled()) {
-			log.debug("writeMap");
-		}
-
-		final Set set = map.entrySet();
-		// NOTE: we need to encode maps as objects for shared objects
-		out.writeStartObject(null);
-		Iterator it = set.iterator();
-		boolean isBeanMap = (map instanceof BeanMap);
-		while (it.hasNext()) {
-			Map.Entry entry = (Map.Entry) it.next();
-			if (isBeanMap && ((String) entry.getKey()).equals("class")) {
-				continue;
-			}
-			out.writeItemKey(entry.getKey().toString());
-			serialize(out, entry.getValue());
-			if (it.hasNext()) {
-				out.markPropertySeparator();
-			}
-		}
-		out.markEndObject();
-	}
-
-}
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/server/net/remoting/RemotingClient.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/server/net/remoting/RemotingClient.java (revision 1606)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/server/net/remoting/RemotingClient.java (revision 1639)
@@ -152,10 +152,5 @@
 		tmp.setAutoExpand(true);
 		Output tmpOut = new Output(tmp);
-		Serializer serializer = new Serializer();
-		tmpOut.writeStartArray(params.length);
-		for (Object param : params) {
-			serializer.serialize(tmpOut, param);
-		}
-		tmpOut.markEndArray();
+		tmpOut.writeArray(params, new Serializer());
 		tmp.flip();
 
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java (revision 1608)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java (revision 1639)
@@ -25,9 +25,16 @@
 import org.red5.io.amf.AMF;
 import org.red5.io.object.DataTypes;
+import org.red5.io.object.Deserializer;
+import org.red5.io.object.RecordSet;
+import org.red5.io.object.RecordSetPage;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.TimeZone;
 
@@ -44,4 +51,12 @@
      */
 	protected static Log log = LogFactory.getLog(Input.class.getName());
+	/**
+	 * Set to a value above <tt>0</tt> to enforce AMF3 decoding mode.
+	 */
+	private int amf3_mode;
+	/**
+	 * List of string values found in the input stream.
+	 */
+	private List<String> stringReferences;
 
 	/**
@@ -52,4 +67,6 @@
 	public Input(ByteBuffer buf) {
 		super(buf);
+		amf3_mode = 0;
+		stringReferences = new ArrayList<String>();
 	}
 
@@ -70,5 +87,5 @@
 		if (currentDataType == AMF.TYPE_AMF3_OBJECT) {
 			currentDataType = buf.get();
-		} else {
+		} else if (amf3_mode == 0) {
 			// AMF0 object
 			return readDataType(currentDataType);
@@ -163,10 +180,11 @@
 	public String readString() {
 		int len = readAMF3Integer();
-		if (len == 0)
+		if (len == 1)
+			// Empty string
 			return "";
 		
 		if ((len & 1) == 0) {
 			// Reference
-			return (String) getReference(len >> 1);
+			return stringReferences.get(len >> 1);
 		}
 		len >>= 1;
@@ -176,5 +194,5 @@
 		final String string = AMF3.CHARSET.decode(strBuf).toString();
 		buf.limit(limit); // Reset the limit
-		storeReference(string);
+		stringReferences.add(string);
 		return string;
 	}
@@ -186,20 +204,13 @@
 	 */
 	public Date readDate() {
-		/*
-		 * Date: 0x0B T7 T6 .. T0 Z1 Z2 T7 to T0 form a 64 bit Big Endian number
-		 * that specifies the number of nanoseconds that have passed since
-		 * 1/1/1970 0:00 to the specified time. This format is ÒUTC 1970Ó. Z1 an
-		 * Z0 for a 16 bit Big Endian number indicating the indicated timeÕs
-		 * timezone in minutes.
-		 */
+		int ref = readAMF3Integer();
+		if ((ref & 1) == 0) {
+			// Reference to previously found date
+			return (Date) getReference(ref >> 1);
+		}
+		
 		long ms = (long) buf.getDouble();
-		short clientTimeZoneMins = buf.getShort();
-		ms += clientTimeZoneMins * 60 * 1000;
-		Calendar cal = new GregorianCalendar();
-		cal.setTime(new Date(ms - TimeZone.getDefault().getRawOffset()));
-		Date date = cal.getTime();
-		if (cal.getTimeZone().inDaylightTime(date)) {
-			date.setTime(date.getTime() - cal.getTimeZone().getDSTSavings());
-		}
+		Date date = new Date(ms);
+		storeReference(date);
 		return date;
 	}
@@ -212,123 +223,97 @@
 	 * @return int        Length of array
 	 */
-	public int readStartArray() {
-		System.err.println("MISSING: readStartArray");
-		return buf.getInt();
-	}
-
-	/**
-	 * Skips elements TODO
-	 */
-	public void skipElementSeparator() {
-		// SKIP
-	}
-
-	/**
-	 * Skips end array TODO
-	 */
-	public void skipEndArray() {
-		// SKIP
-	}
-
+    public Object readArray(Deserializer deserializer) {
+		int count = readAMF3Integer();
+		if ((count & 1) == 0) {
+			// Reference
+			return getReference(count >> 1);
+		}
+		
+		count = (count >> 1);
+		String key = readString();
+		amf3_mode += 1;
+		Object result = null;
+		if (key.equals("")) {
+			// normal array
+			List<Object> resultList = new ArrayList<Object>(count);
+			storeReference(resultList);
+			for (int i=0; i<count; i++) {
+				final Object value = deserializer.deserialize(this);
+				resultList.add(value);
+			}
+			result = resultList;
+		} else {
+			// associative array
+			Map<Object, Object> resultMap = new HashMap<Object, Object>();
+			storeReference(resultMap);
+			while (!key.equals("")) {
+				final Object value = deserializer.deserialize(this);
+				resultMap.put(key, value);
+				key = readString();
+			}
+			for (int i=0; i<count; i++) {
+				final Object value = deserializer.deserialize(this);
+				resultMap.put(i, value);
+			}
+			result = resultMap;
+		}
+		amf3_mode -= 1;
+		return result;			
+	}
+
+    public Object readMap(Deserializer deserializer) {
+    	throw new RuntimeException("AMF3 doesn't support maps.");
+    }
+    
 	// Object
 
-	/**
-	 * Reads start list
-	 * 
-	 * @return int        Size of map
-	 */
-	public int readStartMap() {
-		System.err.println("MISSING: readStartMap");
-		return buf.getInt();
-	}
-
-	/**
-	 * Returns a boolean stating whether this has more items
-	 * 
-	 * @return boolean    <code>true</code> if this Input's buffer has more items, <code>false</code> otherwise
-	 */
-	public boolean hasMoreItems() {
-		return hasMoreProperties();
-	}
-
-	/**
-	 * Reads the item index
-	 * 
-	 * @return int        Array item index
-	 */
-	public String readItemKey() {
-		return readString();
-	}
-
-	/**
-	 * Skips item seperator
-	 */
-	public void skipItemSeparator() {
-		// SKIP
-	}
-
-	/**
-	 * Skips end list
-	 */
-	public void skipEndMap() {
-		skipEndObject();
-	}
-
-	// Object
-
-	/**
-	 * Reads start object
-	 * 
-	 * @return String
-	 */
-	public String readStartObject() {
-		System.err.println("MISSING: readStartObject");
-		return null;
-	}
-
-	/**
-	 * Returns a boolean stating whether there are more properties
-	 * 
-	 * @return boolean       <code>true</code> if there are more properties to read, <code>false</code> otherwise
-	 */
-	public boolean hasMoreProperties() {
-		boolean isEnd = (buf.get() == 0);
-		buf.position(buf.position()-1);
-		return isEnd;
-	}
-
-	/**
-	 * Reads property name
-	 * 
-	 * @return String       Object property name
-	 */
-	public String readPropertyName() {
-		return readString();
-	}
-
-	/**
-	 * Skips property seperator
-	 */
-	public void skipPropertySeparator() {
-		// SKIP
-	}
-
-	/**
-	 * Skips end object
-	 */
-	public void skipEndObject() {
-		buf.skip(1);
-	}
-
+    public Object readObject(Deserializer deserializer) {
+		int type = readAMF3Integer();
+		if ((type & 1) == 0) {
+			// Reference
+			return getReference(type >> 1);
+		}
+		
+		type >>= 1;
+		boolean inlineClass = (type & 1) == 1;
+		if (!inlineClass) {
+			throw new RuntimeException("Class references not supported yet.");
+		}
+		
+		type >>= 1;
+		String className = readString();
+		Object result = null;
+		amf3_mode += 1;
+		if ("".equals(className)) {
+			// "anonymous" object, load as Map
+			Map<String, Object> resultMap = new HashMap<String, Object>();
+			storeReference(resultMap);
+			switch (type & 0x03) {
+			case AMF3.TYPE_OBJECT_PROPERTY:
+				break;
+			case AMF3.TYPE_OBJECT_ANONYMOUS_PROPERTY:
+				resultMap.put("", deserializer.deserialize(this));
+				break;
+			case AMF3.TYPE_OBJECT_VALUE:
+				String key = readString();
+				while (!"".equals(key)) {
+					Object value = deserializer.deserialize(this);
+					resultMap.put(key, value);
+					key = readString();
+				}
+				break;
+			default:
+			case AMF3.TYPE_OBJECT_UNKNOWN:
+				throw new RuntimeException("Unknown object type: " + (type & 0x03));
+			}
+			result = resultMap;
+		} else {
+			throw new RuntimeException("Objects of type " + className + " not supported yet.");
+		}
+		amf3_mode -= 1;
+		return result;
+    }
+    
 	// Others
-
-	/**
-	 * Reads xml
-	 * 
-	 * @return String     XML as String
-	 */
-	public String readXML() {
-		return readString();
-	}
 
 	/**
@@ -342,9 +327,15 @@
 	}
 
+	/** {@inheritDoc} */
+	public Object readReference() {
+		throw new RuntimeException("AMF3 doesn't support direct references.");
+	}
+
 	/**
 	 * Resets map
 	 */
 	public void reset() {
-		this.clearReferences();
+		super.reset();
+		stringReferences.clear();
 	}
 
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java (revision 1606)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java (revision 1639)
@@ -20,5 +20,12 @@
  */
 
+import java.lang.reflect.Array;
+import java.util.Collection;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
@@ -26,4 +33,5 @@
 import org.apache.mina.common.ByteBuffer;
 import org.red5.io.amf.AMF;
+import org.red5.io.object.Serializer;
 
 /**
@@ -40,4 +48,13 @@
 
 	/**
+	 * Set to a value above <tt>0</tt> to disable writing of the AMF3 object tag.
+	 */
+	private int amf3_mode;
+	/**
+	 * List of strings already written.
+	 * */
+	private List<String> stringReferences;
+	
+	/**
 	 * Constructor of AMF3 output.
 	 *
@@ -48,27 +65,40 @@
 	public Output(ByteBuffer buf) {
 		super(buf);
-	}
+		amf3_mode = 0;
+		stringReferences = new LinkedList<String>();
+	}
+	
     /** {@inheritDoc} */
 	public boolean supportsDataType(byte type) {
 		return true;
-	}// Basic Data Types
+	}
+	
+	// Basic Data Types
+	
+	protected void writeAMF3() {
+		if (amf3_mode == 0)
+			buf.put(AMF.TYPE_AMF3_OBJECT);
+	}
+	
     /** {@inheritDoc} */
 	public void writeBoolean(Boolean bol) {
-		buf.put(AMF.TYPE_AMF3_OBJECT);
+		writeAMF3();
 		buf.put(bol ? AMF3.TYPE_BOOLEAN_TRUE : AMF3.TYPE_BOOLEAN_FALSE);
 	}
+	
     /** {@inheritDoc} */
 	public void writeNull() {
-		buf.put(AMF.TYPE_AMF3_OBJECT);
+		writeAMF3();
 		buf.put(AMF3.TYPE_NULL);
 	}
+	
     /** {@inheritDoc} */
 	protected void putInteger(long value) {
 		if (value < 0) {
-			System.err.println("MISSING: negative integer");
-			return;
-		}
-
-		if (value <= 0x7f) {
+			buf.put((byte) (0x80 | ((value >> 22) & 0xff)));
+			buf.put((byte) (0x80 | ((value >> 15) & 0x7f)));
+			buf.put((byte) (0x80 | ((value >> 8) & 0x7f)));
+			buf.put((byte) (value & 0xff));
+		} else if (value <= 0x7f) {
 			buf.put((byte) value);
 		} else if (value <= 0x3fff) {
@@ -86,106 +116,201 @@
 		}
 	}
+	
 	/** {@inheritDoc} */
-	protected void putString(java.nio.ByteBuffer string) {
+	protected void putString(String str, java.nio.ByteBuffer string) {
 		final int len = string.limit();
-		// XXX: Support references!
+		int pos = stringReferences.indexOf(str);
+		if (pos >= 0) {
+			// Reference to existing string
+			putInteger(pos << 1);
+			return;
+		}
+		
 		putInteger(len << 1 | 1);
 		buf.put(string);
 	}
+	
     /** {@inheritDoc} */
 	public void putString(String string) {
+		if ("".equals(string)) {
+			// Empty string;
+			putInteger(1);
+			return;
+		}
+		
 		final java.nio.ByteBuffer strBuf = AMF3.CHARSET.encode(string);
-		putString(strBuf);
-	}
+		putString(string, strBuf);
+	}
+	
     /** {@inheritDoc} */
 	public void writeNumber(Number num) {
+		writeAMF3();
 		if (num instanceof Long || num instanceof Integer || num instanceof Short || num instanceof Byte) {
-			buf.put(AMF.TYPE_AMF3_OBJECT);
 			buf.put(AMF3.TYPE_INTEGER);
 			putInteger(num.longValue());
 		} else {
-			buf.put(AMF.TYPE_AMF3_OBJECT);
 			buf.put(AMF3.TYPE_NUMBER);
 			buf.putDouble(num.doubleValue());
 		}
 	}
+	
     /** {@inheritDoc} */
 	public void writeString(String string) {
-		final java.nio.ByteBuffer strBuf = AMF3.CHARSET.encode(string);
-		buf.put(AMF.TYPE_AMF3_OBJECT);
+		writeAMF3();
 		buf.put(AMF3.TYPE_STRING);
-		putString(strBuf);
-	}
+		if ("".equals(string)) {
+			putInteger(1);
+		} else {
+			final java.nio.ByteBuffer strBuf = AMF3.CHARSET.encode(string);
+			putString(string, strBuf);
+		}
+	}
+	
     /** {@inheritDoc} */
 	public void writeDate(Date date) {
-		buf.put(AMF.TYPE_AMF3_OBJECT);
+		writeAMF3();
 		buf.put(AMF3.TYPE_DATE);
-		// XXX: Support references!
+		if (hasReference(date)) {
+			putInteger(getReferenceId(date) << 1);
+			return;
+		}
+		
+		storeReference(date);
 		putInteger(1);
 		buf.putDouble(date.getTime());
 	}
-    /** {@inheritDoc} */
-	public void writeStartArray(int length) {
-		System.err.println("MISSING: writeStartArray");
-	}
-    /** {@inheritDoc} */
-	public void markElementSeparator() {
-		System.err.println("MISSING: markElementSeparator");
-	}
-    /** {@inheritDoc} */
-	public void markEndArray() {
-		System.err.println("MISSING: markEndArray");
-	}
-    /** {@inheritDoc} */
-	public void writeStartMap(int size) {
-		buf.put(AMF.TYPE_AMF3_OBJECT);
+	
+    /** {@inheritDoc} */
+    public void writeArray(Collection array, Serializer serializer) {
+		writeAMF3();
 		buf.put(AMF3.TYPE_ARRAY);
-		// XXX: Support references
-		putInteger(size << 1 | 1);
-	}
-    /** {@inheritDoc} */
-	public void writeItemKey(String key) {
-		putString(key);
-	}
-    /** {@inheritDoc} */
-	public void markItemSeparator() {
-		System.err.println("MISSING: markItemSeparator");
-	}
-    /** {@inheritDoc} */
-	public void markEndMap() {
-		System.err.println("MISSING: markEndMap");
-	}
-    /** {@inheritDoc} */
-	public void writeStartObject(String classname) {
-		System.err.println("MISSING: writeStartObject");
-	}
-    /** {@inheritDoc} */
-	public void writeStartObject(String classname, int numMembers) {
-		buf.put(AMF.TYPE_AMF3_OBJECT);
-		buf.put(AMF3.TYPE_OBJECT);
-		// XXX: support object and classname references
-		int value = numMembers << 4 | AMF3.TYPE_OBJECT_VALUE | 1;
-		putInteger(value);
-		if (classname == null) {
-			putInteger(0);
-		} else {
-			putString(classname);
-		}
-	}
-    /** {@inheritDoc} */
-	public void writePropertyName(String name) {
-		putString(name);
-	}
-    /** {@inheritDoc} */
-	public void markPropertySeparator() {
-
-	}
-    /** {@inheritDoc} */
-	public void markEndObject() {
-		putInteger(0);
-	}
+    	if (hasReference(array)) {
+    		putInteger(getReferenceId(array) << 1);
+    		return;
+    	}
+    	
+    	storeReference(array);
+		amf3_mode += 1;
+		int count = array.size();
+		putInteger(count << 1 | 1);
+		putString("");
+		for (Object item: array) {
+			serializer.serialize(this, item);
+		}
+		amf3_mode -= 1;
+    }
+
+    /** {@inheritDoc} */
+    public void writeArray(Object[] array, Serializer serializer) {
+		writeAMF3();
+		buf.put(AMF3.TYPE_ARRAY);
+    	if (hasReference(array)) {
+    		putInteger(getReferenceId(array) << 1);
+    		return;
+    	}
+    	
+    	storeReference(array);
+		amf3_mode += 1;
+		int count = array.length;
+		putInteger(count << 1 | 1);
+		putString("");
+		for (Object item: array) {
+			serializer.serialize(this, item);
+		}
+		amf3_mode -= 1;
+    }
+
+    /** {@inheritDoc} */
+    public void writeArray(Object array, Serializer serializer) {
+		writeAMF3();
+		buf.put(AMF3.TYPE_ARRAY);
+    	if (hasReference(array)) {
+    		putInteger(getReferenceId(array) << 1);
+    		return;
+    	}
+    	
+    	storeReference(array);
+		amf3_mode += 1;
+		int count = Array.getLength(array);
+		putInteger(count << 1 | 1);
+		putString("");
+		for (int i=0; i<count; i++) {
+			serializer.serialize(this, Array.get(array, i));
+		}
+		amf3_mode -= 1;
+    }
+
+    /** {@inheritDoc} */
+    public void writeMap(Map<Object, Object> map, Serializer serializer) {
+    	if (hasReference(map)) {
+    		writeAMF3();
+    		buf.put(AMF3.TYPE_ARRAY);
+    		putInteger(getReferenceId(map) << 1);
+    		return;
+    	}
+    	
+    	storeReference(map);
+		// Search number of starting integer keys
+		int count = 0;
+		for (int i=0; i<map.size(); i++) {
+			if (map.containsKey(i)) {
+				count++;
+			} else {
+				break;
+			}
+		}
+		
+		writeAMF3();
+		buf.put(AMF3.TYPE_ARRAY);
+		amf3_mode += 1;
+		if (count == map.size()) {
+			// All integer keys starting from zero: serialize as regular array
+			putInteger(count << 1 | 1);
+			putString("");
+			for (int i=0; i<count; i++) {
+				serializer.serialize(this, map.get(i));
+			}
+			amf3_mode -= 1;
+			return;
+		}
+		
+		putInteger(count << 1 | 1);
+		// Serialize key-value pairs first
+		for (Map.Entry<Object, Object> entry: map.entrySet()) {
+			Object key = entry.getKey();
+			if ((key instanceof Number) && !(key instanceof Float) && !(key instanceof Double) &&
+					((Number) key).longValue() >= 0 && ((Number) key).longValue() < count) {
+				// Entry will be serialized later
+				continue;
+			}
+			putString(key.toString());
+			serializer.serialize(this, entry.getValue());
+		}
+		putString("");
+		// Now serialize integer keys starting from zero
+		for (int i=0; i<count; i++) {
+			serializer.serialize(this, map.get(i));
+		}
+		amf3_mode -= 1;
+    }
+
+    /** {@inheritDoc} */
+    public void writeMap(Collection array, Serializer serializer) {
+    	writeString("Not implemented.");
+    }
+
+    /** {@inheritDoc} */
+    public void writeObject(Object object, Serializer serializer) {
+    	writeString("Not implemented.");
+    }
+
+    /** {@inheritDoc} */
+    public void writeObject(Map<Object, Object> map, Serializer serializer) {
+    	writeMap(map, serializer);
+    }
+
     /** {@inheritDoc} */
 	public void writeXML(String xml) {
-		buf.put(AMF.TYPE_AMF3_OBJECT);
+		writeAMF3();
 		buf.put(AMF3.TYPE_XML);
 		putString(xml);
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/AMF3.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/AMF3.java (revision 1608)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/AMF3.java (revision 1639)
@@ -48,12 +48,12 @@
 
     /**
+     * Boolean false marker
+     */
+    public static final byte TYPE_BOOLEAN_FALSE = 0x02;
+
+    /**
      * Boolean true marker
      */
-    public static final byte TYPE_BOOLEAN_TRUE = 0x02;
-
-    /**
-     * Boolean false marker
-     */
-    public static final byte TYPE_BOOLEAN_FALSE = 0x03;
+    public static final byte TYPE_BOOLEAN_TRUE = 0x03;
 
     /**
@@ -122,10 +122,10 @@
 	 * is equal to the number of properties in the class-def.
 	 */
-    public static final byte TYPE_OBJECT_VALUE = 0x0A;
+    public static final byte TYPE_OBJECT_VALUE = 0x02;
 
 	/**
 	 * 11 = unseen / unknown
 	 */
-    public static final byte TYPE_OBJECT_UNKNOWN = 0x0B;
+    public static final byte TYPE_OBJECT_UNKNOWN = 0x03;
 
 }
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/flv/impl/FLVReader.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/flv/impl/FLVReader.java (revision 1613)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/flv/impl/FLVReader.java (revision 1639)
@@ -30,4 +30,5 @@
 import org.red5.io.flv.FLVHeader;
 import org.red5.io.flv.IKeyFrameDataAnalyzer;
+import org.red5.io.object.Serializer;
 import org.red5.io.utils.IOUtils;
 
@@ -38,4 +39,5 @@
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
@@ -461,8 +463,7 @@
 
         // Duration property
-        out.writeString("onMetaData");
-		out.writeStartMap(3);
-		out.writePropertyName("duration");
-		out.writeNumber(duration / 1000.0);
+		out.writeString("onMetaData");
+		Map<String, Object> props = new HashMap<String, Object>();
+		props.put("duration", duration / 1000.0);
 		if (firstVideoTag != -1) {
 			long old = getCurrentPosition();
@@ -472,6 +473,5 @@
 			byte frametype = in.get();
             // Video codec id
-            out.writePropertyName("videocodecid");
-			out.writeNumber(frametype & MASK_VIDEO_CODEC);
+			props.put("videocodecid", frametype & MASK_VIDEO_CODEC);
 			setCurrentPosition(old);
 		}
@@ -483,11 +483,9 @@
 			byte frametype = in.get();
             // Audio codec id
-            out.writePropertyName("audiocodecid");
-			out.writeNumber((frametype & MASK_SOUND_FORMAT) >> 4);
+            props.put("audiocodecid", (frametype & MASK_SOUND_FORMAT) >> 4);
 			setCurrentPosition(old);
 		}
-		out.writePropertyName("canSeekToEnd");
-		out.writeBoolean(true);
-		out.markEndMap();
+		props.put("canSeekToEnd", true);
+		out.writeObject(props, new Serializer());
 		buf.flip();
 
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/mock/Input.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/mock/Input.java (revision 1606)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/mock/Input.java (revision 1639)
@@ -22,8 +22,11 @@
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 im

Note:
Diffs are chopped if more than 25k.
This is to get past the limit on the mailing list.



More information about the Red5commits mailing list