<NME> AACADTSPacketizer.java
<BEF> /*
 * Copyright (C) 2011-2015 GUIGUI Simon, fyhertz@gmail.com
 *
 * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.majorkernelpanic.streaming.rtp;

import java.io.IOException;
import net.majorkernelpanic.streaming.audio.AACStream;
import android.os.SystemClock;
import android.util.Log;

/**
 *   
 *   RFC 3640.  
 *
 *   This packetizer must be fed with an InputStream containing ADTS AAC. 
 *   AAC will basically be rewrapped in an RTP stream and sent over the network.
 *   This packetizer only implements the aac-hbr mode (High Bit-rate AAC) and
 *   each packet only carry a single and complete AAC access unit.
 * 
 */
public class AACADTSPacketizer extends AbstractPacketizer implements Runnable {

	private final static String TAG = "AACADTSPacketizer";

	private final static String TAG = "AACADTSPacketizer";

	private Thread t;
	private Statistics stats = new Statistics();

	public AACADTSPacketizer() throws IOException {
		super();
		}
	}

	public void stop() {
		if (t != null) {
			try {
				is.close();
			} catch (IOException ignore) {}
			t.interrupt();
			try {
				t.join();
			} catch (InterruptedException e) {}
			t = null;
		}
	}

	public void setSamplingRate(int samplingRate) {
		this.samplingRate = samplingRate;
		} catch (InterruptedException e) {}
	}

	public void run() {

		// Adts header fields that we need to parse
		boolean protection;
		int frameLength;
		long ts=0, oldtime = SystemClock.elapsedRealtime(), now = oldtime;

		try {

				// Synchronisation: ADTS packet starts with 12bits set to 1
				while (true) {
					if ( (is.read()&0xFF) == 0xFF ) {
						header[1] = (byte) is.read();
						if ( (header[1]&0xF0) == 0xF0) break;
					}
				}

				// Parse adts header (ADTS packets start with a 7 or 9 byte long header)
				fill(header, 2, 5);

				// The protection bit indicates whether or not the header contains the two extra bytes
				protection = (header[1]&0x01)>0 ? true : false;
				frameLength = (header[3]&0x03) << 11 | 
						(buffer[rtphl+4]&0xFF) << 3 | 
						(buffer[rtphl+5]&0xFF) >> 5 ;
				frameLength -= (protection ? 7 : 9);

				//Log.d(TAG,"frameLength: "+frameLength+" protection: "+protection);

				// Read CRS if any
				if (!protection) is.read(buffer,rtphl,2);

				// Read frame
				is.read(buffer,rtphl+4,frameLength);

				// AU-headers-length field: contains the size in bits of a AU-header
				// 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta 
				// 13 bits will be enough because ADTS uses 13 bits for frame length
				buffer[rtphl] = 0;
				buffer[rtphl+1] = 0x10; 

				// AU-size
				buffer[rtphl+2] = (byte) (frameLength>>5);
				buffer[rtphl+3] = (byte) (frameLength<<3);

				// AU-Index
				buffer[rtphl+3] &= 0xF8;
				buffer[rtphl+3] |= 0x00;

				socket.markNextPacket();

				now = SystemClock.elapsedRealtime();
				stats.push(now-oldtime);
				oldtime = now;
				ts += stats.average()*90;
				oldtime = now;
				socket.updateTimestamp(ts);
				socket.send(rtphl+frameLength+4);

			}
		} catch (IOException e) {
		} catch (ArrayIndexOutOfBoundsException e) {
			Log.e(TAG,"ArrayIndexOutOfBoundsException: "+(e.getMessage()!=null?e.getMessage():"unknown error"));
			e.printStackTrace();
		} finally {
			running = false;
		}

	}

}
			else sum+=len;
		}
		return sum;
	}

}

<MSG> Some bug fixes in the AACADTSPacketizer. Some modifications in Session & SessionManager. Http server rewrote.

<DFF> @@ -39,8 +39,12 @@ public class AACADTSPacketizer extends AbstractPacketizer implements Runnable {
 
 	private final static String TAG = "AACADTSPacketizer";
 
+	// Maximum size of RTP packets
+	private final static int MAXPACKETSIZE = 1400;
+	
 	private Thread t;
 	private Statistics stats = new Statistics();
+	private int samplingRate = 8000;
 
 	public AACADTSPacketizer() throws IOException {
 		super();
@@ -65,11 +69,21 @@ public class AACADTSPacketizer extends AbstractPacketizer implements Runnable {
 		} catch (InterruptedException e) {}
 	}
 
+	public void setSamplingRate(int samplingRate) {
+		this.samplingRate = samplingRate;
+	}
+	
 	public void run() {
 
+		// "A packet SHALL carry either one or more complete Access Units, or a
+		// single fragment of an Access Unit.  Fragments of the same Access Unit
+		// have the same time stamp but different RTP sequence numbers.  The
+		// marker bit in the RTP header is 1 on the last fragment of an Access
+		// Unit, and 0 on all other fragments." RFC 3640
+		
 		// Adts header fields that we need to parse
 		boolean protection;
-		int frameLength;
+		int frameLength, sum, length, nbau, nbpk;
 		long ts=0, oldtime = SystemClock.elapsedRealtime(), now = oldtime;
 
 		try {
@@ -91,48 +105,75 @@ public class AACADTSPacketizer extends AbstractPacketizer implements Runnable {
 						(buffer[rtphl+4]&0xFF) << 3 | 
 						(buffer[rtphl+5]&0xFF) >> 5 ;
 				frameLength -= (protection ? 7 : 9);
-
-				//Log.d(TAG,"frameLength: "+frameLength+" protection: "+protection);
-
+				
+				// Number of AAC frames in the ADTS frame
+				nbau = (buffer[rtphl+6]&0x03) + 1;
+				
+				// The number of RTP packets that will be sent for this ADTS frame
+				nbpk = frameLength/MAXPACKETSIZE + 1;
+				
 				// Read CRS if any
 				if (!protection) is.read(buffer,rtphl,2);
 
-				// Read frame
-				is.read(buffer,rtphl+4,frameLength);
-
-				// AU-headers-length field: contains the size in bits of a AU-header
-				// 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta 
-				// 13 bits will be enough because ADTS uses 13 bits for frame length
-				buffer[rtphl] = 0;
-				buffer[rtphl+1] = 0x10; 
-
-				// AU-size
-				buffer[rtphl+2] = (byte) (frameLength>>5);
-				buffer[rtphl+3] = (byte) (frameLength<<3);
-
-				// AU-Index
-				buffer[rtphl+3] &= 0xF8;
-				buffer[rtphl+3] |= 0x00;
-
-				socket.markNextPacket();
-
 				now = SystemClock.elapsedRealtime();
 				stats.push(now-oldtime);
 				oldtime = now;
-				ts += stats.average()*90;
+				ts +=  (nbau*1024*1000 / samplingRate )*90; // FIXME: 1024 seems to work better on certain players...
 				oldtime = now;
 				socket.updateTimestamp(ts);
-				socket.send(rtphl+frameLength+4);
+				
+				sum = 0;
+				while (sum<frameLength) {
+
+					// Read frame
+					if (frameLength-sum > MAXPACKETSIZE-rtphl-4) {
+						length = MAXPACKETSIZE-rtphl-4;
+					}
+					else {
+						length = frameLength-sum;
+						socket.markNextPacket();
+					}
+					sum += length;
+					is.read(buffer,rtphl+4, length);
+
+					// AU-headers-length field: contains the size in bits of a AU-header
+					// 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta 
+					// 13 bits will be enough because ADTS uses 13 bits for frame length
+					buffer[rtphl] = 0;
+					buffer[rtphl+1] = 0x10; 
+
+					// AU-size
+					buffer[rtphl+2] = (byte) (frameLength>>5);
+					buffer[rtphl+3] = (byte) (frameLength<<3);
+
+					// AU-Index
+					buffer[rtphl+3] &= 0xF8;
+					buffer[rtphl+3] |= 0x00;
+
+					//Log.d(TAG,"frameLength: "+frameLength+" protection: "+protection+ " length: "+length);
+										
+					// We wait before calling send() so that we won't send too many packets at once
+					//Log.d(TAG,"SLEEP: "+ ( 2*nbau*1024*1000 / (3*nbpk*samplingRate) ) );
+					Thread.sleep( ( 2*nbau*1024*1000 / (3*nbpk*samplingRate) ) );
+					
+					socket.send(rtphl+4+length);
+
+				}
 
 			}
 		} catch (IOException e) {
+			// Ignore
 		} catch (ArrayIndexOutOfBoundsException e) {
 			Log.e(TAG,"ArrayIndexOutOfBoundsException: "+(e.getMessage()!=null?e.getMessage():"unknown error"));
 			e.printStackTrace();
+		} catch (InterruptedException e) {
+			// Ignore
 		} finally {
 			running = false;
 		}
 
+		Log.d(TAG,"AAC packetizer stopped !");
+		
 	}
 
 }

Some bug fixes in the AACADTSPacketizer. Some modifications in Session & SessionManager. Http server rewrote.