Streaming
Media and Data Streaming.
logo armasuisse     logo armasuisse
 Other Projects :  <NESD>    <Neural>
Project Summary Concept Screenshots Download/Demos Links

Project Description

During the last project iteration (2nd half of 2007), we study ways to stream raw data along with video and audio. The key point is to keep data in sync with the video in a precise manner. The domain of application is wide, and we focus us on geo-localisation of sensors : for example streaming the position and field of view of a camera in real-time in order to store geo-localized video or project it back in a 3D environnment.
 
We use live555 library, to stream data and plan to customize a player in order to support live streaming (for example VLC Media player)

Protocol Parsing with Java

We also used to work with Javatm (Rich client, RCP client, server,..). We face in numerous situations where we have to parse headers in protocols, files.. which is seemly easier in C/C++ using struct.  
There is no equivalent to structs in Java, but Java5 bring us annotations, which could be used to emulate them. Note that we are aware that the solution shown here is not a replacement, leak features, performs probably bad and slow.. but we (pretentious java fanboys) think it's still worth 2 minutes to look at it.  

Annotations

Our proposal is to define a class with public fields in order to model the header to parse. All fields in class are described with a new annotation BitArrayField that describe the field with its positioning and it length (in bit). In our example we took the RTP header specified in RFC 3550.
 
package rtp;

import org.dreier.bitarray.BitArrayField;

/**
 * RTP packet header.
 * @author Frederic Dreier
 *
 */
public class RtpHeaderBA {
	
	@BitArrayField(offset = 0, length = 2)
	public int version;

	@BitArrayField(offset = 2, length = 1)
	public boolean padding;

	@BitArrayField(offset = 3, length = 1)
	public boolean extension;

	@BitArrayField(offset = 4, length = 4)
	public int ccCsrc;
	
	@BitArrayField(offset = 8, length = 1)
	public boolean marker;

	@BitArrayField(offset = 9, length = 7)
	public int payloadType;

	@BitArrayField(offset = 16, length = 16)
	public int sequenceNbr;

	@BitArrayField(offset = 32, length = 32)
	public long timestamp;

	@BitArrayField(offset = 64, length = 32)
	public long ssrc;

	@BitArrayField(offset = 96, length = 32)
	public long csrc;

}
 
Then using it is straightforward just put your data in a byte array and give it for parsing. No config needed, no XML. The header will be initialized with the buffer data.  
 
		byte[] data = ...;

		// prepare empty structure
		RtpHeaderBA header = new RtpHeaderBA();

		// extract it
		BitExtractor b = new BitExtractor();
		b.parse(data, header);
		
		// plenty of place for other code here !! 
		...
 
To run this example all you need is the BitArrayField annotation and the BitExtractor class. We provide you our quick and dirty implementation.
 
The BitArrayField is kept as simple as possible as you will see:
 
/* 
 * (C) Copyright 2007-2008, by Frederic Dreier
 *
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU 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 General Public 
 * License for more details.
 *
 * You should have received a copy of the GNU 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.
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 */
package org.dreier.bitarray;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)

/**
 * Annotation used by BitExtractor.
 * @author dreier Frederic Dreier
 */
public @interface BitArrayField {
	int offset();

	int length();

	boolean unsigned() default true;
}

 
The BitExtractor is a little bit more complex. It could be improved, extended, refactored.. Or just use it has is :-)
 
/* 
 * (C) Copyright 2007-2008, by Frederic Dreier
 *
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU 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 General Public 
 * License for more details.
 *
 * You should have received a copy of the GNU 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.
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 */
package org.dreier.bitarray;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Quick and Dirty implementation of a Bit Extractor.
 *  
 * @author Frederic Dreier
 */
public class BitExtractor {
	private static final int[] MASK_A = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };

	private static final int[] MASK_P = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00 };

	private HashMap mapping = new HashMap();

	public MetaData register(Class clazz) {
		synchronized (mapping) {
			MetaData md = mapping.get(clazz);
			if (md == null) {
				// retieve annotations for this class
				md = new MetaData();
				for (Field fld : clazz.getDeclaredFields()) {
					int cnt = 0;
					for (Annotation ant : fld.getAnnotations()) {
						if (ant instanceof BitArrayField) {
							// check consistency.
							if (cnt > 0) {
								throw new RuntimeException("Field[" + fld.getName() + "] of class [" + clazz
										+ "] should only have one annotation of type 'BitArrayField'.");
							}
							cnt++;
							// update metadata
							BitArrayField bfAnt = (BitArrayField) ant;
							MetaField mf = new MetaField(bfAnt.offset(), bfAnt.length(), bfAnt.unsigned(), fld
									.getType(), fld);
							md.add(mf);
						}
					}
				}
				// cache meta data
				mapping.put(clazz, md);
			}
			return md;
		}
	}

	/**
	 * Parse data hand update header.
	 * 
	 * @param data
	 * @param struct
	 */
	public void parse(byte[] data, Object struct) {
		// get MetaData
		MetaData md = register(struct.getClass());
		for (MetaField f : md.fields) {
			if (f.type.equals(int.class) || f.type.equals(long.class)) {
				// integer field...

				int bByte = f.offset / 8;
				int bMask = MASK_A[f.offset % 8];
				int end = f.offset + f.length;
				int pads = 8 - (f.offset + f.length) % 8;
				int eByte = end / 8;
				// first byte
				long acc = data[bByte] & bMask;
				if (bByte == eByte) {
					acc = acc >> pads;
				} else {
					// loop bytes
					for (int ptr = bByte + 1; ptr < eByte; ptr++) {
						acc = (acc << 8) | (data[ptr] & 0xFF);
					}
					// last byte (could be smaller than 8 bits)
					acc = (data[eByte] & MASK_P[pads] | (acc << 8)) >> pads;
				}
				// set value in struct
				try {
					if (f.type.equals(int.class)) {
						// int
						f.classField.setInt(struct, (int) acc);
					} else {
						// long
						f.classField.setLong(struct, acc);
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			} else if (f.type.equals(boolean.class)) {
				// boolean
				if (f.length != 1) {
					throw new RuntimeException("field [" + f.classField.getName()
							+ "] is a boolean and should have a length of '1'.");
				}
				int bIdx = f.offset / 8;
				int b = f.offset - bIdx * 8;
				int mask = 1 << (7 - b);
				int acc = data[bIdx] & mask;
				// set value in struct
				try {
					f.classField.setBoolean(struct, acc != 0);
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			} else if (f.type.equals(String.class)) {
				// String (only on plain byte)
				if (f.length % 8 != 0) {
					throw new RuntimeException("field [" + f.classField.getName()
							+ "] is a String and should have a length of 'n*8'.");
				}
				if (f.offset % 8 != 0) {
					throw new RuntimeException("field [" + f.classField.getName()
							+ "] is a String and should have an offset of 'n*8'.");
				}
				int charCnt = f.length / 8;
				int bByte = f.offset / 8;
				String result = new String(data, bByte, charCnt);
				// set value in struct
				try {
					f.classField.set(struct, result);
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			} else {
				throw new RuntimeException("Unsupported class [" + f.type + "]");
			}
		}
	}

	// ================================================================
	// Inner classes
	// ================================================================
	/**
	 * A class meta data.
	 * 
	 * @author Frederic Dreier
	 */
	public class MetaData {
		private ArrayList fields = new ArrayList();

		/**
		 * Add a field. Covenient method
		 * 
		 * @param field
		 */
		public void add(MetaField field) {
			fields.add(field);
		}

		/**
		 * Get fields.
		 * 
		 * @return
		 */
		public List getFields() {
			return fields;
		}
	}

	/**
	 * Bit array field.
	 * 
	 * @author dreier
	 */
	public class MetaField {
		private int offset;

		private int length;

		private Field classField;

		private boolean unsigned;

		private Class type;

		public MetaField(int offset, int length, boolean unsigned, Class type, Field classField) {
			this.offset = offset;
			this.length = length;
			this.unsigned = unsigned;
			this.type = type;
			this.classField = classField;

		}

		public int getLength() {
			return length;
		}

		public int getOffset() {
			return offset;
		}

		public Class getType() {
			return type;
		}

		public boolean isUnsigned() {
			return unsigned;
		}

	}

	
}


 
 

Screenshots

-

Downloads

-

Links

Armasuisse
Frédéric Dreier AI/AL Homepage
Annotation in Java (SUN)