[Red5devs] [red5 commit] r3064 - in java/server/branches/paulg_mp4: conf src/org/red5/io/mp4 src/org/red5/io/mp4/impl src/...

codesite-noreply at google.com codesite-noreply at google.com
Wed Sep 17 16:56:00 PDT 2008


Author: mondain
Date: Wed Sep 17 16:55:08 2008
New Revision: 3064

Modified:
    java/server/branches/paulg_mp4/conf/logback.xml
    java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Atom.java
    java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Frame.java
    java/server/branches/paulg_mp4/src/org/red5/io/mp4/impl/MP4Reader.java
     
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java
     
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java
     
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/VideoData.java
    java/server/branches/paulg_mp4/src/org/red5/server/stream/PlayEngine.java
     
java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/MP4FrameTest.java

Log:
Timestamps and frame collection sort are now working and match Izumi  
exactly. Updated timestamps to use doubles as well as audio sample rate.  
Added debug sections for RTMP packet checking as this seems to be a  
difference between us and Izumi.

Modified: java/server/branches/paulg_mp4/conf/logback.xml
==============================================================================
--- java/server/branches/paulg_mp4/conf/logback.xml	(original)
+++ java/server/branches/paulg_mp4/conf/logback.xml	Wed Sep 17 16:55:08 2008
@@ -49,7 +49,7 @@
  		<level value="DEBUG" />
  	</logger>
  	<logger name="org.red5.io.mp4.MP4Atom">
-		<level value="WARN" />
+		<level value="DEBUG" />
  	</logger>
  	<logger name="org.red5.io.mp4.MP4Descriptor">
  		<level value="WARN" />
@@ -117,6 +117,12 @@
  	</logger>	
  	<logger name="org.red5.server.net.rtmp.codec">
  		<level value="INFO" />
+	</logger>
+	<logger name="org.red5.server.net.rtmp.codec.RTMPProtocolDecoder">
+		<level value="DEBUG" />
+	</logger>
+	<logger name="org.red5.server.net.rtmp.codec.RTMPProtocolEncoder">
+		<level value="DEBUG" />
  	</logger>
  	<logger name="org.red5.server.net.rtmp.status">
  		<level value="INFO" />

Modified: java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Atom.java
==============================================================================
--- java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Atom.java	 
(original)
+++ java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Atom.java	Wed Sep  
17 16:55:08 2008
@@ -19,26 +19,21 @@
   */

  /**
-	This software module was originally developed by Apple Computer, Inc.
-	in the course of development of MPEG-4.
-	This software module is an implementation of a part of one or
-	more MPEG-4 tools as specified by MPEG-4.
-	ISO/IEC gives users of MPEG-4 free license to this
-	software module or modifications thereof for use in hardware
-	or software products claiming conformance to MPEG-4.
-	Those intending to use this software module in hardware or software
-	products are advised that its use may infringe existing patents.
-	The original developer of this software module and his/her company,
-	the subsequent editors and their companies, and ISO/IEC have no
-	liability for use of this software module or modifications thereof
-	in an implementation.
-	Copyright is not released for non MPEG-4 conforming
-	products. Apple Computer, Inc. retains full right to use the code for its  
own
-	purpose, assign or donate the code to a third party and to
-	inhibit third parties from using the code for non
-	MPEG-4 conforming products.
-	This copyright notice must be included in all copies or
-	derivative works. Copyright (c) 1999.
+	This software module was originally developed by Apple Computer, Inc. in  
the
+	course of development of MPEG-4. This software module is an  
implementation of
+	a part of one or more MPEG-4 tools as specified by MPEG-4. ISO/IEC gives  
users
+	of MPEG-4 free license to this software module or modifications thereof  
for
+	use in hardware or software products claiming conformance to MPEG-4. Those
+	intending to use this software module in hardware or software products are
+	advised that its use may infringe existing patents. The original  
developer of
+	this software module and his/her company, the subsequent editors and their
+	companies, and ISO/IEC have no liability for use of this software module  
or
+	modifications thereof in an implementation. Copyright is not released for  
non
+	MPEG-4 conforming products. Apple Computer, Inc. retains full right to  
use the
+	code for its own purpose, assign or donate the code to a third party and  
to
+	inhibit third parties from using the code for non MPEG-4 conforming  
products.
+	This copyright notice must be included in all copies or	derivative works.
+	Copyright (c) 1999.
  */

  import java.io.IOException;
@@ -786,7 +781,7 @@
  		log.debug("V Resolution: {}", verticalRez);
  		bitstream.skipBytes(4);
  		int frameCount = (int) bitstream.readBytes(2);
-		log.debug("Frame count: {}", frameCount);
+		log.debug("Frame to sample count: {}", frameCount);
  		int stringLen = (int) bitstream.readBytes(1);
  		log.debug("String length (cpname): {}", stringLen);
  		String compressorName = bitstream.readString(31);

Modified: java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Frame.java
==============================================================================
--- java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Frame.java	 
(original)
+++ java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Frame.java	Wed  
Sep 17 16:55:08 2008
@@ -20,7 +20,7 @@
   */

  /**
- * Represents an MP4 frame.
+ * Represents an MP4 frame / chunk sample
   *
   * @author Paul Gregoire (mondain at gmail.com)
   */
@@ -32,7 +32,7 @@

  	private int size;

-	private int time;
+	private double time;

  	private boolean keyFrame;

@@ -80,11 +80,11 @@
  	 *
  	 * @return
  	 */
-	public int getTime() {
+	public double getTime() {
  		return time;
  	}

-	public void setTime(int time) {
+	public void setTime(double time) {
  		this.time = time;
  	}

@@ -101,6 +101,46 @@
  		this.keyFrame = keyFrame;
  	}

+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + (int) (offset ^ (offset >>> 32));
+		result = prime * result + type;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		MP4Frame other = (MP4Frame) obj;
+		if (offset != other.offset)
+			return false;
+		if (type != other.type)
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder("MP4Frame type=");
+		sb.append(type);
+		sb.append(", time=");
+		sb.append(time);
+		sb.append(", size=");
+		sb.append(size);
+		sb.append(", offset=");
+		sb.append(offset);
+		sb.append(", keyframe=");
+		sb.append(keyFrame);
+		return sb.toString();
+	}
+
  	/**
  	 * The frames are expected to be sorted by their timestamp
  	 */
@@ -110,6 +150,10 @@
  			ret = 1;
  		} else if (this.time < that.getTime()) {
  			ret = -1;
+		} else if (this.time == that.getTime() && this.offset >  
that.getOffset()) {
+			ret = 1;
+		} else if (this.time == that.getTime() && this.offset <  
that.getOffset()) {
+			ret = -1;			
  		}
  		return ret;
  	}

Modified:  
java/server/branches/paulg_mp4/src/org/red5/io/mp4/impl/MP4Reader.java
==============================================================================
--- java/server/branches/paulg_mp4/src/org/red5/io/mp4/impl/MP4Reader.java	 
(original)
+++ java/server/branches/paulg_mp4/src/org/red5/io/mp4/impl/MP4Reader.java	 
Wed Sep 17 16:55:08 2008
@@ -135,10 +135,11 @@
  	private int timeScale;
  	private int width;
  	private int height;
-	private int audioSampleRate;
+	private double audioSampleRate;
  	private int audioChannels;
  	private int videoSampleCount;
  	private double fps;
+	private double videoSampleRate = 2997.0; //not sure where to get this  
value from?
  	private int avcLevel;
  	private int avcProfile;
  	private String formattedDuration;
@@ -170,6 +171,8 @@
  	private long audioCount;
  	private long videoCount;
  	
+	private double baseTs = 0f;
+	
  	/**
  	 * Container for metadata and any other tags that should
  	 * be sent prior to media data.
@@ -208,7 +211,7 @@
  		//build the keyframe meta data
  		analyzeKeyFrames();
  		//add meta data
-		firstTags.add(createFileMeta());
+		//firstTags.add(createFileMeta());
  		//create / add the pre-streaming tags
  		createPreStreamingTags();
  	}
@@ -350,9 +353,9 @@
      												setAudioCodecId(MP4Atom.intToType(mp4a.getType()));
      												//log.debug("{}",  
ToStringBuilder.reflectionToString(mp4a));
      												log.debug("Sample size: {}", mp4a.getSampleSize());								 
		
-    												audioSampleRate = mp4a.getTimeScale();
+    												audioSampleRate = mp4a.getTimeScale() * 1.0;
      												audioChannels = mp4a.getChannelCount();
-    												log.debug("Sample rate: {}", audioSampleRate);			
+    												log.debug("Sample rate (time scale): {}",  
audioSampleRate);			
      												log.debug("Channels: {}", audioChannels);										
      												/* no data we care about right now
      												//mp4a: esds
@@ -465,6 +468,7 @@
      												MP4Atom avc1 = stsd.getChildren().get(0);
      												//could set the video codec here
      												setVideoCodecId(MP4Atom.intToType(avc1.getType()));
+    												log.debug("Sample rate (time scale): {}", videoSampleRate);
      												//
      												MP4Atom avcC = avc1.lookup(MP4Atom.typeToInt("avcC"), 0);
      												if (avcC != null) {
@@ -545,6 +549,7 @@
      					//real duration
      					StringBuilder sb = new StringBuilder();
      					double videoTime = ((double) duration / (double) timeScale);
+    					log.debug("Video time: {}", videoTime);
      					int minutes = (int) (videoTime / 60);
      					if (minutes > 0) {
      		    			sb.append(minutes);
@@ -851,6 +856,13 @@
      		(byte) 0x8c, (byte) 0x18, (byte) 0x9c, (byte) 0x01, (byte) 0,     
(byte) 0x04, (byte) 0x68,
      		(byte) 0xce, (byte) 0x3c, (byte) 0x80});
      		
+    		//fake avcc
+    		//(byte) 0x01, (byte) 0x4D, (byte) 0x40, (byte) 0x1F, (byte) 0xFF,  
(byte) 0xE1, (byte) 0x00,
+    		//(byte) 0x14, (byte) 0x27, (byte) 0x4D, (byte) 0x40, (byte) 0x1F,  
(byte) 0xA9, (byte) 0x18,
+    		//(byte) 0x0A, (byte) 0x00, (byte) 0x8B, (byte) 0x60, (byte) 0x0D,  
(byte) 0x41, (byte) 0x80,
+    		//(byte) 0x41, (byte) 0x8C, (byte) 0x2B, (byte) 0x5E, (byte) 0xF7,  
(byte) 0xC0, (byte) 0x40,
+    		//(byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x28, (byte) 0xCE,  
(byte) 0x09, (byte) 0xC8
+
      		body.flip();
      		tag.setBody(body);

@@ -882,21 +894,22 @@
  			// Return first tags before media data
  			return firstTags.removeFirst();
  		}		
-		log.debug("Read tag - currentSample {}, prevFrameSize {}", new  
Object[]{currentSample, prevFrameSize});
+		log.debug("Read tag - sample {} prevFrameSize {} audio: {} video: {}",  
new Object[]{currentSample, prevFrameSize, audioCount, videoCount});
  		
  		//get the current frame
  		MP4Frame frame = frames.get(currentSample - 1);
+		log.debug("Playback {}", frame);
+		
  		int sampleSize = frame.getSize();
-		//int sampleSize = (Integer) videoSamples.get(currentSample) + 5;
-		int ts = frame.getTime();
-		//int ts = videoSampleDuration * currentSample;
-		log.debug("Read tag - sampleSize {} ts {}", new Object[]{sampleSize,  
ts});
-		log.debug("Read tag - sample dur / scale {}", new  
Object[]{((currentSample * timeScale) / videoSampleDuration)});		
+		
+		double frameTs = (frame.getTime() - baseTs) * 1000.0;
+		int time = (int) Math.round(frameTs);
+		log.debug("Read tag - dst: {} base: {} time: {}", new Object[]{frameTs,  
baseTs, time});
+		//log.debug("Read tag - sampleSize {} ts {}", new Object[]{sampleSize,  
ts});
+		//log.debug("Read tag - sample dur / scale {}", new  
Object[]{((currentSample * timeScale) / videoSampleDuration)});		
  		
  		long samplePos = frame.getOffset();
-		//long samplePos = samplePosMap.get(currentSample);
-		log.debug("Read tag - samplePos {}", samplePos);
-		log.debug("Sample position map: {}", samplePosMap);
+		//log.debug("Read tag - samplePos {}", samplePos);

  		//determine frame type and packet body padding
  		byte type = frame.getType();
@@ -937,7 +950,7 @@
  		ByteBuffer payload = getChunkedPayload(data.array());		
  		
  		//create the tag
-		ITag tag = new Tag(type, ts, payload.limit(), payload, prevFrameSize);
+		ITag tag = new Tag(type, time, payload.limit(), payload, prevFrameSize);
  		log.debug("Read tag - type: {} body size: {}", (type ==  
TYPE_AUDIO ? "Audio" : "Video"), tag.getBodySize());
  		
  		//increment the sample number
@@ -945,12 +958,14 @@
  		//set the frame / tag size
  		prevFrameSize = tag.getBodySize();
  	
+		baseTs += frameTs / 1000.0;
  		//log.debug("Tag: {}", tag);
  		return tag;
  	}

      /**
-     * Performs frame analysis and generates metadata for use in seeking.
+     * Performs frame analysis and generates metadata for use in seeking.  
The
+     * method name is a little misleading since it analyzes all the frames.
  	 *
       * @return             Keyframe metadata
       */
@@ -960,13 +975,13 @@
  			return keyframeMeta;
  		}
  		log.debug("Analyzing key frames");
-			
+					
  		//key frame sample numbers are stored in the syncSamples collection
  		int keyframeCount = syncSamples.size();
  		
          // Lists of video positions and timestamps
          List<Long> positionList = new ArrayList<Long>(keyframeCount);
-        List<Integer> timestampList = new  
ArrayList<Integer>(keyframeCount);
+        List<Float> timestampList = new ArrayList<Float>(keyframeCount);
          // Maps positions to tags
          posTagMap = new HashMap<Long, Integer>();
          samplePosMap = new HashMap<Integer, Long>();
@@ -981,18 +996,18 @@
  			log.debug("Video first chunk: {} count:{}", firstChunk, sampleCount);
  			pos = (Long) videoChunkOffsets.elementAt(firstChunk - 1);
  			while (sampleCount > 0) {
-				log.debug("Position: {}", pos);
+				//log.debug("Position: {}", pos);
      			posTagMap.put(pos, sample);
      			samplePosMap.put(sample, pos);
  				//calculate ts
-				int ts = ((int) videoSampleDuration * sample);
+    			double ts = (videoSampleDuration * (sample - 1)) / videoSampleRate;
      			//check to see if the sample is a keyframe
      			boolean keyframe = syncSamples.contains(sample);
      			if (keyframe) {
      				log.debug("Keyframe - sample: {}", sample);
      				positionList.add(pos);
      				//log.debug("Keyframe - timestamp: {}", ts);
-    				timestampList.add(ts);
+    				//timestampList.add(ts);
      			}
      			int size = ((Integer) videoSamples.get(sample - 1)).intValue();

@@ -1005,6 +1020,8 @@
      			frame.setType(TYPE_VIDEO);
      			frames.add(frame);
      			
+    			log.debug("Sample #{} {}", sample, frame);
+    			
      			//inc and dec stuff
      			pos += size;
      			sampleCount--;
@@ -1012,10 +1029,10 @@
  			}
  		}

-		log.debug("Position Tag Map size: {}", posTagMap.size());
-		log.debug("Keyframe position list size: {}", positionList.size());
-		
-		//add the audio frames / samples / chunks
+		log.debug("Position map size: {} keyframe list size: {}",  
posTagMap.size(), positionList.size());
+		log.debug("Sample position map (video): {}", samplePosMap);
+			
+		//add the audio frames / samples / chunks		
  		sample = 1;
  		records = audioSamplesToChunks.elements();
  		while (records.hasMoreElements()) {
@@ -1026,7 +1043,7 @@
  			pos = (Long) audioChunkOffsets.elementAt(firstChunk - 1);
  			while (sampleCount > 0) {
      			//calculate ts
-    			int ts = ((int) audioSampleDuration * sample);
+				double ts = (audioSampleDuration * (sample - 1)) / audioSampleRate;
      			//sample size
      			int size = ((Integer) audioSamples.get(sample - 1)).intValue();
      			//create a frame
@@ -1037,6 +1054,8 @@
          		frame.setType(TYPE_AUDIO);
          		frames.add(frame);
          		
+    			log.debug("Sample #{} {}", sample, frame);
+    			
      			//inc and dec stuff
      			pos += size;
      			sampleCount--;
@@ -1044,25 +1063,31 @@
              }		
  		}

+		records = null;
+		
  		//sort the frames
  		Collections.sort(frames);
  		
+		log.debug("Frames count (expect 16042 for backcountry): {}",  
frames.size());
+		log.debug("Frames: {}", frames);
+		
  		keyframeMeta = new KeyFrameMeta();
  		keyframeMeta.duration = duration;
+		/*
  		posTimeMap = new HashMap<Long, Long>();

  		keyframeMeta.positions = new long[positionList.size()];
-		keyframeMeta.timestamps = new int[timestampList.size()];
+		keyframeMeta.timestamps = new float[timestampList.size()];
  		for (int i = 0; i < keyframeMeta.positions.length; i++) {
  			keyframeMeta.positions[i] = positionList.get(i);
  			keyframeMeta.timestamps[i] = timestampList.get(i);
-			posTimeMap.put((long) positionList.get(i), (long) timestampList
+			posTimeMap.put((long) positionList.get(i), (float) timestampList
  					.get(i));
  		}
  		if (keyframeCache != null) {
  			keyframeCache.saveKeyFrameMeta(file, keyframeMeta);
  		}
-		
+		*/
  		return keyframeMeta;
  	}

@@ -1073,9 +1098,9 @@
       * @return
       */
      private ByteBuffer getChunkedPayload(byte[] payload) {
-    	log.debug("Get chunked payload");
+    	//log.debug("Get chunked payload");
      	int len = payload.length;
-    	log.debug("Payload length: {}", len);
+    	//log.debug("Payload length: {}", len);
      	int chunkLen = 0;
      	int offset = 0;
      	//extra bytes needed for chunk markers and such
@@ -1086,12 +1111,12 @@
      	ret.setAutoExpand(true);
      	while (len > 0) {
          	chunkLen = Math.min(len, 4096);
-        	log.debug("Chunk len: {}", chunkLen);
+        	//log.debug("Chunk len: {}", chunkLen);
          	ret.put(payload, offset, chunkLen);
-        	log.debug("read: {}", ret.position());
+        	//log.debug("read: {}", ret.position());
          	offset += chunkLen;
          	len -= chunkLen;
-        	log.debug("len: {}", len);
+        	//log.debug("len: {}", len);
          	if (len > 0) {
          		ret.put(CHUNK_MARKER);
          	}

Modified:  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java
==============================================================================
---  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java	 
(original)
+++  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java	 
Wed Sep 17 16:55:08 2008
@@ -441,6 +441,7 @@
  	public Header decodeHeader(ByteBuffer in, Header lastHeader) {

  		byte headerByte = in.get();
+		log.debug("Header byte: {}", headerByte);
  		int headerValue;
  		int byteCount = 1;
  		if ((headerByte & 0x3f) == 0) {
@@ -530,10 +531,11 @@
  				message = decodeInvoke(in, rtmp);
  				break;
  			case TYPE_NOTIFY:
-				if (header.getStreamId() == 0)
+				if (header.getStreamId() == 0) {
  					message = decodeNotify(in, header, rtmp);
-				else
+				} else {
  					message = decodeStreamMetadata(in);
+				}
  				break;
  			case TYPE_PING:
  				message = decodePing(in);
@@ -770,11 +772,13 @@
  	private boolean isStreamCommand(String action) {
  		return (ACTION_CREATE_STREAM.equals(action)
  				|| ACTION_DELETE_STREAM.equals(action)
-				|| ACTION_PUBLISH.equals(action) || ACTION_PLAY.equals(action)
-				|| ACTION_SEEK.equals(action) || ACTION_PAUSE.equals(action)
+				|| ACTION_PUBLISH.equals(action)
+				|| ACTION_PLAY.equals(action)
+				|| ACTION_SEEK.equals(action)
+				|| ACTION_PAUSE.equals(action)
  				|| ACTION_CLOSE_STREAM.equals(action)
-				|| ACTION_RECEIVE_VIDEO.equals(action) || ACTION_RECEIVE_AUDIO
-				.equals(action));
+				|| ACTION_RECEIVE_VIDEO.equals(action)
+				|| ACTION_RECEIVE_AUDIO.equals(action));
  	}

  	/**

Modified:  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java
==============================================================================
---  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java	 
(original)
+++  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java	 
Wed Sep 17 16:55:08 2008
@@ -236,6 +236,7 @@
       */
      public void encodeHeader(Header header, Header lastHeader, ByteBuffer  
buf) {
  		final byte headerType = getHeaderType(header, lastHeader);
+		log.debug("Header byte (type): {}", headerType);
  		RTMPUtils.encodeHeaderByte(buf, headerType, header
  				.getChannelId());


Modified:  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/VideoData.java
==============================================================================
---  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/VideoData.java	 
(original)
+++  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/VideoData.java	 
Wed Sep 17 16:55:08 2008
@@ -131,7 +131,7 @@
  		frameType = (FrameType) in.readObject();
  		byte[] byteBuf = (byte[]) in.readObject();
  		if (byteBuf != null) {
-			data = ByteBuffer.allocate(0);
+			data = ByteBuffer.allocate(byteBuf.length);
  			data.setAutoExpand(true);
  			SerializeUtils.ByteArrayToByteBuffer(byteBuf, data);
  		}

Modified:  
java/server/branches/paulg_mp4/src/org/red5/server/stream/PlayEngine.java
==============================================================================
---  
java/server/branches/paulg_mp4/src/org/red5/server/stream/PlayEngine.java	 
(original)
+++  
java/server/branches/paulg_mp4/src/org/red5/server/stream/PlayEngine.java	 
Wed Sep 17 16:55:08 2008
@@ -351,6 +351,7 @@
  					if (stream != null && stream.getCodecInfo() != null) {
  						IVideoStreamCodec videoCodec = stream.getCodecInfo()
  								.getVideoCodec();
+						log.debug("Stream video codec: {}", videoCodec);
  						if (videoCodec != null) {
  							ByteBuffer keyFrame = videoCodec.getKeyframe();
  							if (keyFrame != null) {
@@ -372,6 +373,7 @@
  									videoFrameDropper.reset();
  								} finally {
  									video.release();
+									video = null;
  								}
  							}
  						}
@@ -439,13 +441,11 @@
  						if (msg == null) {
  							break;
  						}
-						if (!(msg instanceof RTMPMessage)) {
-							continue;
+						if (msg instanceof RTMPMessage) {
+							body = ((RTMPMessage) msg).getBody();
  						}
-						body = ((RTMPMessage) msg).getBody();
  					}
  				}
-
  				if (body != null) {
  					// Adjust timestamp when playing lists
  					body.setTimestamp(body.getTimestamp() + timestampOffset);

Modified:  
java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/MP4FrameTest.java
==============================================================================
---  
java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/MP4FrameTest.java	 
(original)
+++  
java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/MP4FrameTest.java	 
Wed Sep 17 16:55:08 2008
@@ -24,23 +24,43 @@
  		//create some frames
  		MP4Frame frame1 = new MP4Frame();
  		frame1.setTime(1);
+		frame1.setOffset(1);
  		frames.add(frame1);
  		
  		MP4Frame frame2 = new MP4Frame();
  		frame2.setTime(6);
+		frame2.setOffset(6);
  		frames.add(frame2);
  		
  		MP4Frame frame3 = new MP4Frame();
  		frame3.setTime(660);
+		frame3.setOffset(660);
  		frames.add(frame3);
  		
  		MP4Frame frame4 = new MP4Frame();
  		frame4.setTime(3);
+		frame4.setOffset(3);
  		frames.add(frame4);
  		
  		MP4Frame frame5 = new MP4Frame();
  		frame5.setTime(400);
+		frame5.setOffset(400);		
  		frames.add(frame5);
+
+		MP4Frame frame6 = new MP4Frame();
+		frame6.setTime(1000);
+		frame6.setOffset(1010);
+		frames.add(frame6);
+		
+		MP4Frame frame7 = new MP4Frame();
+		frame7.setTime(1000);
+		frame7.setOffset(1000);
+		frames.add(frame7);		
+
+		MP4Frame frame8 = new MP4Frame();
+		frame8.setTime(1000);
+		frame8.setOffset(900);
+		frames.add(frame8);			
  		
  		System.out.printf("Frame 1 - time: %d (should be 660)\n",  
frames.get(2).getTime());

@@ -50,7 +70,7 @@
  		
  		int f = 1;
  		for (MP4Frame frame : frames) {
-			System.out.printf("Frame %d - time: %d\n", f++, frame.getTime());
+			System.out.printf("Frame %d - time: %d offset: %d\n", f++,  
frame.getTime(), frame.getOffset());
  		}
  		
  	}



More information about the Red5devs mailing list