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

Daniel Rossi spam at electroteque.org
Tue Sep 16 00:10:23 PDT 2008


yay how is this going.

On 16/09/2008, at 4:43 PM, codesite-noreply at google.com wrote:

> Author: mondain
> Date: Mon Sep 15 23:42:07 2008
> New Revision: 3061
>
> Added:
>    java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Frame.java
>
> java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/ 
> MP4FrameTest.java
> Modified:
>    java/server/branches/paulg_mp4/conf/logback.xml
>    java/server/branches/paulg_mp4/conf/red5-common.xml
>    java/server/branches/paulg_mp4/conf/red5.properties
>    java/server/branches/paulg_mp4/conf/web.xml
>    java/server/branches/paulg_mp4/java6_build_dist.bat
>    java/server/branches/paulg_mp4/src/org/red5/io/mp4/impl/ 
> MP4Reader.java
>    java/server/branches/paulg_mp4/src/org/red5/server/api/Red5.java
>
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/ 
> RTMPHandler.java
>
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/ 
> FLVData.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
>
> Log:
> Refactoring mp4 handling to more closely match Izumi style of media  
> handling
>
> 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	Mon Sep 15  
> 23:42:07 2008
> @@ -1,232 +1,229 @@
>  <?xml version="1.0" encoding="UTF-8" ?>
>  <configuration>
> -	<consolePlugin/>
> -	<appender name="CONSOLE"  
> class="ch.qos.logback.core.ConsoleAppender">
> +	<appender name="CONSOLE"
> +		class="ch.qos.logback.core.ConsoleAppender">
> +		<filter class="ch.qos.logback.classic.filter.LevelFilter">
> +      		<level>WARN</level>
> +      		<onMatch>ACCEPT</onMatch>
> +      		<onMismatch>DENY</onMismatch>
> +    	</filter>		
>  		<layout class="ch.qos.logback.classic.PatternLayout">
> -			<Pattern>[%p] [%thread] %logger - %msg%n</Pattern>
> +			<Pattern>[%p] %d{ISO8601} %t:( %c.%M ) %m%n</Pattern>
>  		</layout>
>  	</appender>
>  	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
> -		<File>../log/red5.log</File>
> +		<File>log/red5.log</File>
>  		<Append>false</Append>
> -		<Encoding>UTF-8</Encoding>
>  		<BufferedIO>false</BufferedIO>
>  		<ImmediateFlush>true</ImmediateFlush>
>  		<layout class="ch.qos.logback.classic.PatternLayout">
> -			<Pattern>%d{ISO8601} [%thread] %-5level %logger{35} - %msg%n</ 
> Pattern>
> +			<Pattern>[%p] %d{ISO8601} [%thread] %logger{21} - %msg%n</Pattern>
>  		</layout>
>  	</appender>
>  	<appender name="ERRORFILE" class="ch.qos.logback.core.FileAppender">
> -		<File>../log/error.log</File>
> +		<File>log/error.log</File>
>  		<Append>false</Append>
> -		<Encoding>UTF-8</Encoding>
>  		<BufferedIO>false</BufferedIO>
>  		<ImmediateFlush>true</ImmediateFlush>
> -		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
> -			<level>WARN</level>
> -		</filter>
> +    	<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
> +      		<level>WARN</level>
> +    	</filter>
>  		<layout class="ch.qos.logback.classic.PatternLayout">
> -			<Pattern>%d{ISO8601} [%thread] %-5level %logger{35} - %msg%n</ 
> Pattern>
> +			<Pattern>[%p] %d{ISO8601} [%thread] %logger{35} - %msg%n</Pattern>
>  		</layout>
>  	</appender>
>  	<root>
> -		<level value="DEBUG"/>
> -		<appender-ref ref="CONSOLE"/>
> -		<appender-ref ref="FILE"/>
> -		<appender-ref ref="ERRORFILE"/>
> +		<level value="DEBUG" />
> +		<appender-ref ref="ERRORFILE" />
> +		<appender-ref ref="FILE" />
> +		<appender-ref ref="CONSOLE" />
>  	</root>
> +	<logger name="com.infrared5">
> +		<level value="DEBUG" />
> +	</logger>	
>  	<!-- Red5 -->
> -	
>  	<logger name="org.red5.io">
> -		<level value="DEBUG"/>
> +		<level value="INFO" />
>  	</logger>
> -	
> -	<logger name="org.red5.io.object">
> -		<level value="WARN"/>
> +	<logger name="org.red5.io.mp4">
> +		<level value="DEBUG" />
>  	</logger>
> +	<logger name="org.red5.io.mp4.MP4Atom">
> +		<level value="WARN" />
> +	</logger>
> +	<logger name="org.red5.io.mp4.MP4Descriptor">
> +		<level value="WARN" />
> +	</logger>
> +	<logger name="org.red5.server.net.rtmp.status.StatusObject">
> +		<level value="DEBUG" />
> +	</logger>	
>  	<logger name="org.red5.server">
> -		<level value="WARN"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.Client">
> -		<level value="INFO"/>
> +		<level value="DEBUG" />
> +	</logger>
> +	<logger name="org.red5.server.CoreHandler">
> +		<level value="DEBUG" />
> +	</logger>
> +	<logger name="org.red5.server.Scope">
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.jetty">
> -		<level value="INFO"/>
> +		<level value="DEBUG" />
>  	</logger>
>  	<logger name="org.red5.server.Standalone">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.tomcat">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.api.stream.support">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.cache">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
> -	<logger name="org.red5.server.jetty.Red5WebPropertiesConfiguration">
> -		<level value="WARN"/>
> +	<logger
> +		name="org.red5.server.jetty.Red5WebPropertiesConfiguration">
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.jmx">
> -		<level value="INFO"/>
> +		<level value="OFF" />
>  	</logger>
>  	<logger name="org.red5.server.messaging.InMemoryPushPushPipe">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.net">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.net.servlet.RTMPTServlet">
> -		<level value="WARN"/>
> -	</logger>
> +		<level value="WARN" />
> +	</logger>	
>  	<logger name="org.red5.server.net.servlet">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.net.proxy">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.net.remoting">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.net.rtmp">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.red5.server.net.rtmp.RTMPHandler">
> -		<level value="OFF"/>
> -	</logger>
> -	<logger name="org.red5.server.net.rtmp.BaseRTMPHandler">
> -		<level value="OFF"/>
> -	</logger>
> +		<level value="WARN" />
> +	</logger>	
> +	<logger name="org.red5.server.net.rtmp.RTMPMinaTransport">
> +		<level value="DEBUG" />
> +	</logger>	
>  	<logger name="org.red5.server.net.rtmp.codec">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
> -	<logger name="org.red5.server.net.rtmp.RTMPMinaIoHandler">
> -		<level value="OFF"/>
> +	<logger name="org.red5.server.net.rtmp.status">
> +		<level value="INFO" />
>  	</logger>
> -	<logger name="org.red5.server.net.rtmp.RTMPMinaTransport">
> -		<level value="INFO"/>
> +	<logger name="org.red5.server.net.mrtmp">
> +		<level value="DEBUG" />
>  	</logger>
> -	<logger name="org.red5.server.net.rtmp.status">
> -		<level value="INFO"/>
> +	<logger name="org.red5.server.net.mrtmp.codec">
> +		<level value="DEBUG" />
>  	</logger>
>  	<logger name="org.red5.server.net.rtmpt">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.persistence">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.pooling.ThreadObjectFactory">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.script">
> -		<level value="WARN"/>
> -	</logger>
> +		<level value="WARN" />
> +	</logger>	
>  	<logger name="org.red5.server.service">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.red5.server.so">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.red5.server.stream">
> -		<level value="INFO"/>
> +		<level value="DEBUG" />
>  	</logger>
> -
>  	<logger name="org.red5.server.stream.ProviderService">
> -		<level value="DEBUG"/>
> -	</logger>
> -	<logger name="org.red5.server.stream.provider">
> -		<level value="DEBUG"/>
> +		<level value="DEBUG" />
>  	</logger>
> -
>  	<logger name="org.red5.server.stream.consumer">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
> -	<logger name="org.red5.server.net.mrtmp">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.red5.server.net.mrtmp.codec">
> -		<level value="WARN"/>
> -	</logger>
> -	<!-- Mina -->
> +	<!-- Red5 demos -->
> +	<logger name="org.red5.server.webapp.oflaDemo">
> +		<level value="DEBUG" />
> +	</logger>	
> +	<!-- Mina -->			
>  	<logger name="org.apache.mina">
> -		<level value="WARN"/>
> -	</logger>
> +		<level value="WARN" />
> +	</logger>	
>  	<logger name="org.apache.mina.filter">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.apache.mina.filter.thread.ThreadPoolFilter">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<!-- Apache commons -->
> -	<logger name="org.apache.commons">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.apache.commons.modeler">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.apache.commons.beanutils">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.apache.commons.digester">
> -		<level value="WARN"/>
> +	<logger name="org.apache">
> +		<level value="INFO" />
>  	</logger>
> -	<logger name="httpclient">
> -		<level value="WARN"/>
> +	<logger name="org.apache.tomcat.util.net">
> +		<level value="INFO" />
>  	</logger>
> -	<!-- Apache catalina / tomcat -->
> -	<logger name="org.apache.catalina">
> -		<level value="INFO"/>
> +	<logger name="org.apache.catalina.deploy.SecurityCollection">
> +		<level value="INFO" />
>  	</logger>
> -	<logger name="org.apache.catalina.authenticator">
> -		<level value="INFO"/>
> +	<logger name="org.apache.catalina.loader.WebappClassLoader">
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.apache.catalina.realm">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.apache.catalina.session">
> -		<level value="WARN"/>
> -	</logger>
> -	<logger name="org.apache.jasper">
> -		<level value="INFO"/>
> +		<level value="WARN" />
>  	</logger>
> -	<logger name="org.apache.tomcat">
> -		<level value="INFO"/>
> +	<logger name="org.apache.tomcat.util.modeler">
> +		<level value="WARN" />
>  	</logger>
> -	<logger name="org.apache.tomcat.util.net">
> -		<level value="WARN"/>
> +	<logger name="org.apache.commons.digester">
> +		<level value="WARN" />
>  	</logger>
>  	<!-- Jetty -->
>  	<logger name="org.mortbay">
> -		<level value="WARN"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.mortbay.log">
> -		<level value="INFO"/>
> +		<level value="WARN" />
>  	</logger>
>  	<!-- Spring -->
>  	<logger name="org.springframework">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.springframework.beans.factory">
> -		<level value="INFO"/>
> -	</logger>
> +		<level value="INFO" />
> +	</logger>	
>  	<logger name="org.springframework.beans.factory.xml">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.springframework.ui.context.support">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.springframework.web.context">
> -		<level value="INFO"/>
> +		<level value="INFO" />
>  	</logger>
>  	<logger name="org.springframework.web.context.support">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<logger name="org.quartz">
> -		<level value="WARN"/>
> +		<level value="WARN" />
>  	</logger>
>  	<!-- Caching -->
>  	<logger name="net.sf.ehcache">
> -		<level value="INFO"/>
> -	</logger>
> +		<level value="INFO" />
> +	</logger>	
> +	<logger name="org.hibernate">
> +		<level value="WARN" />
> +	</logger>	
>  </configuration>
>
> Modified: java/server/branches/paulg_mp4/conf/red5-common.xml
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- java/server/branches/paulg_mp4/conf/red5-common.xml	(original)
> +++ java/server/branches/paulg_mp4/conf/red5-common.xml	Mon Sep 15  
> 23:42:07
> 2008
> @@ -1,9 +1,12 @@
>  <?xml version="1.0" encoding="UTF-8" ?>
>  <beans xmlns="http://www.springframework.org/schema/beans"
> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>  	 xmlns:lang="http://www.springframework.org/schema/lang"
> xsi:schemaLocation="http://www.springframework.org/schema/beans
> http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
> http://www.springframework.org/schema/lang
> http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
> -	<bean id="placeholderConfig"
> class 
> = 
> "org 
> .springframework.beans.factory.config.PropertyPlaceholderConfigurer">
> -		<property name="location" value="classpath:/red5.properties"/>
> -	</bean>
> +
> +	<bean id="placeholderConfig"
> +		
> class 
> = 
> "org 
> .springframework.beans.factory.config.PropertyPlaceholderConfigurer">
> +		<property name="location" value="classpath:/red5.properties" />
> +	</bean>	
> +	
>  	<!-- This context is shared between all child contexts. -->
>  	<!-- Server bean -->
>  	<bean id="red5.server" class="org.red5.server.Server"/>
> @@ -13,11 +16,16 @@
>  	</bean>
>  	<bean id="jmxAgent" class="org.red5.server.jmx.JMXAgent"
> init-method="init">
>  		<!-- The RMI adapter allows remote connections to the MBeanServer  
> -->
> -
>  		<property name="enableRmiAdapter" value="true"/>
>  		<property name="rmiAdapterPort" value="${jmx.rmi.port.registry}"/>
>  		<property name="rmiAdapterRemotePort"
> value="${jmx.rmi.port.remoteobjects}"/>
>  		<property name="rmiAdapterHost" value="${jmx.rmi.host}"/>
> +		<!-- SSL
> +			To use jmx with ssl you must also supply the location of the  
> keystore
> and its password
> +			when starting the server with the following JVM options:
> +				-Djavax.net.ssl.keyStore=keystore
> +     			-Djavax.net.ssl.keyStorePassword=password
> +		-->
>  		<property name="enableSsl" value="${jmx.rmi.ssl}"/>
>  		<!-- Starts a registry if it doesnt exist -->
>  		<property name="startRegistry" value="true"/>
> @@ -94,7 +102,7 @@
>  	</bean>
>  	<!-- High level access to streams -->
>  	<bean id="streamService"  
> class="org.red5.server.stream.StreamService"/>
> -	<!-- High level access to broadcasted streams -->
> +	<!-- Hight level access to broadcasted streams -->
>  	<bean id="providerService"
> class="org.red5.server.stream.ProviderService"/>
>  	<!-- Provides output to consumers -->
>  	<bean id="consumerService"
> class="org.red5.server.stream.ConsumerService"/>
> @@ -208,7 +216,7 @@
>  		the client buffer filled.
>  	-->
>  	<bean id="streamExecutor"
> class="java.util.concurrent.ScheduledThreadPoolExecutor">
> -		<constructor-arg value="4"/>
> +		<constructor-arg value="2"/>
>  		<property name="maximumPoolSize" value="32"/>
>  	</bean>
>  	<!-- ClientBroadcastStream and PlaylistSubscriberStream
>
> Modified: java/server/branches/paulg_mp4/conf/red5.properties
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- java/server/branches/paulg_mp4/conf/red5.properties	(original)
> +++ java/server/branches/paulg_mp4/conf/red5.properties	Mon Sep 15  
> 23:42:07
> 2008
> @@ -1,12 +1,10 @@
>  # Socket policy
>  policy.host=0.0.0.0
>  policy.port=843
> -
>  # HTTP
>  http.host=0.0.0.0
> -http.port=5080
> +http.port=80
>  https.port=443
> -
>  # RTMP
>  rtmp.host=0.0.0.0
>  rtmp.port=1935
>
> Modified: java/server/branches/paulg_mp4/conf/web.xml
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- java/server/branches/paulg_mp4/conf/web.xml	(original)
> +++ java/server/branches/paulg_mp4/conf/web.xml	Mon Sep 15 23:42:07  
> 2008
> @@ -554,6 +554,10 @@
>          <mime-type>text/css</mime-type>
>      </mime-mapping>
>      <mime-mapping>
> +        <extension>dcr</extension>
> +        <mime-type>application/x-director</mime-type>
> +    </mime-mapping>
> +    <mime-mapping>
>          <extension>dib</extension>
>          <mime-type>image/bmp</mime-type>
>      </mime-mapping>
> @@ -1034,6 +1038,10 @@
>          <mime-type>application/x-visio</mime-type>
>      </mime-mapping>
>      <mime-mapping>
> +        <extension>w3d</extension>
> +        <mime-type>application/octet-stream</mime-type>
> +    </mime-mapping>
> +    <mime-mapping>
>          <!-- Wireless Bitmap -->
>          <extension>wbmp</extension>
>          <mime-type>image/vnd.wap.wbmp</mime-type>
> @@ -1153,4 +1161,4 @@
>      <auth-constraint/>
>    </security-constraint>
>
> -</web-app>
> \ No newline at end of file
> +</web-app>
>
> Modified: java/server/branches/paulg_mp4/java6_build_dist.bat
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- java/server/branches/paulg_mp4/java6_build_dist.bat	(original)
> +++ java/server/branches/paulg_mp4/java6_build_dist.bat	Mon Sep 15  
> 23:42:07
> 2008
> @@ -1,7 +1,10 @@
> +SETLOCAL
>
>  SET JAVA_HOME=c:\dev\java6
>  SET PATH=c:\dev\java6\bin;%PATH%
>
> -ant clean dist
> +ant dist
> +
> +ENDLOCAL
>
>  pause
>
> Added: java/server/branches/paulg_mp4/src/org/red5/io/mp4/ 
> MP4Frame.java
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- (empty file)
> +++ java/server/branches/paulg_mp4/src/org/red5/io/mp4/MP4Frame.java	 
> Mon
> Sep 15 23:42:07 2008
> @@ -0,0 +1,117 @@
> +package org.red5.io.mp4;
> +
> +/*
> + * RED5 Open Source Flash Server - http://www.osflash.org/red5
> + *
> + * Copyright (c) 2006-2007 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
> + */
> +
> +/**
> + * Represents an MP4 frame.
> + *
> + * @author Paul Gregoire (mondain at gmail.com)
> + */
> +public class MP4Frame implements Comparable<MP4Frame> {
> +
> +	private byte type;
> +
> +	private long offset;
> +
> +	private int size;
> +
> +	private int time;
> +
> +	private boolean keyFrame;
> +
> +	/**
> +	 * Returns the data type, being audio or video.
> +	 *
> +	 * @return
> +	 */
> +	public byte getType() {
> +		return type;
> +	}
> +
> +	public void setType(byte type) {
> +		this.type = type;
> +	}
> +
> +	/**
> +	 * Returns the offset of the data chunk in the media source.
> +	 *
> +	 * @return
> +	 */
> +	public long getOffset() {
> +		return offset;
> +	}
> +
> +	public void setOffset(long offset) {
> +		this.offset = offset;
> +	}
> +
> +	/**
> +	 * Returns the size of the data chunk.
> +	 *
> +	 * @return
> +	 */
> +	public int getSize() {
> +		return size;
> +	}
> +
> +	public void setSize(int size) {
> +		this.size = size;
> +	}
> +
> +	/**
> +	 * Returns the timestamp.
> +	 *
> +	 * @return
> +	 */
> +	public int getTime() {
> +		return time;
> +	}
> +
> +	public void setTime(int time) {
> +		this.time = time;
> +	}
> +
> +	/**
> +	 * Returns whether or not this chunk represents a key frame.
> +	 *
> +	 * @return
> +	 */
> +	public boolean isKeyFrame() {
> +		return keyFrame;
> +	}
> +
> +	public void setKeyFrame(boolean keyFrame) {
> +		this.keyFrame = keyFrame;
> +	}
> +
> +	/**
> +	 * The frames are expected to be sorted by their timestamp
> +	 */
> +	public int compareTo(MP4Frame that) {
> +		int ret = 0;
> +		if (this.time > that.getTime()) {
> +			ret = 1;
> +		} else if (this.time < that.getTime()) {
> +			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	
> Mon Sep 15 23:42:07 2008
> @@ -26,6 +26,7 @@
>  import java.text.DecimalFormat;
>  import java.text.NumberFormat;
>  import java.util.ArrayList;
> +import java.util.Collections;
>  import java.util.Enumeration;
>  import java.util.HashMap;
>  import java.util.LinkedList;
> @@ -45,26 +46,27 @@
>  import org.red5.io.flv.impl.Tag;
>  import org.red5.io.mp4.MP4Atom;
>  import org.red5.io.mp4.MP4DataStream;
> +import org.red5.io.mp4.MP4Frame;
>  import org.red5.io.object.Serializer;
> -import org.red5.server.api.IConnection;
> -import org.red5.server.api.Red5;
> -import org.red5.server.net.rtmp.Channel;
> -import org.red5.server.net.rtmp.event.Invoke;
> -import org.red5.server.service.PendingCall;
>  import org.slf4j.Logger;
>  import org.slf4j.LoggerFactory;
>
>  /**
> - * A Reader is used to read the contents of a MP4 file.
> - * NOTE: This class is not implemented as threading-safe. The caller
> - * should make sure the threading-safety.
> + * This reader is used to read the contents of an MP4 file.
> + *
> + * NOTE: This class is not implemented as thread-safe, the caller
> + * should ensure the thread-safety.
>   * <p>
>   * New NetStream notifications
>   * <br />
>   * Two new notifications facilitate the implementation of the  
> playback
> components:
>   * <ul>
> - * <li>NetStream.Play.FileStructureInvalid: This event is sent if the
> player detects an MP4 with an invalid file structure. Flash Player  
> cannot
> play files that have invalid file structures.</li>
> - * <li>NetStream.Play.NoSupportedTrackFound: This event is sent if  
> the
> player does not detect any supported tracks. If there aren't any  
> supported
> video, audio or data tracks found, Flash Player does not play the  
> file.</li>
> + * <li>NetStream.Play.FileStructureInvalid: This event is sent if the
> player detects
> + * an MP4 with an invalid file structure. Flash Player cannot play  
> files
> that have
> + * invalid file structures.</li>
> + * <li>NetStream.Play.NoSupportedTrackFound: This event is sent if  
> the
> player does not
> + * detect any supported tracks. If there aren't any supported  
> video, audio
> or data
> + * tracks found, Flash Player does not play the file.</li>
>   * </ul>
>   * </p>
>   *
> @@ -86,6 +88,8 @@
>
>  	/** Video packet prefix for standard frames (interframe)*/
>  	private final static byte[] PREFIX_VIDEO_FRAME = new byte[]{(byte)  
> 0x27,
> (byte) 0x01, (byte) 0, (byte) 0, (byte) 0};
> +	
> +	private final static byte[] CHUNK_MARKER = new byte[]{(byte) 0xc5};
>
>      /**
>       * File
> @@ -118,7 +122,9 @@
>  	private static IKeyFrameMetaCache keyframeCache;
>  	
>  	/** Whether or not the clip contains a video track */
> -	private boolean hasVideo = false;
> +	private boolean hasVideo = false;	
> +	/** Whether or not the clip contains an audio track */
> +	private boolean hasAudio = false;
>  	
>  	//
>  	private String videoCodecId = "avc1";
> @@ -155,10 +161,14 @@
>  	private int audioSampleDuration = 1024;
>  	
>  	//keep track of current sample
> -	private int currentSample = 0;
> +	private int currentSample = 1;
> +	
> +    private int prevFrameSize = 0;
>  	
> -	private long firstAudioTag;
> -	private long firstVideoTag;
> +    private List<MP4Frame> frames = new ArrayList<MP4Frame>();
> +
> +	private long audioCount;
> +	private long videoCount;
>  	
>  	/**
>  	 * Container for metadata and any other tags that should
> @@ -193,7 +203,7 @@
>      	this.file = f;
>  		this.fis = new MP4DataStream(new FileInputStream(f));
>  		channel = fis.getChannel();
> -		//decode all the info from the atoms
> +		//decode all the info that we want from the atoms
>  		decodeHeader();
>  		//build the keyframe meta data
>  		analyzeKeyFrames();
> @@ -204,8 +214,8 @@
>  	}
>
>  	/**
> -	 * Currently this expects the moov atom at the beginning of the  
> file,
> later we will
> -	 * have to handle the mdat at the beginning instead.
> +	 * This handles the moov atom being at the beginning or end of the  
> file,
> so the mdat may also
> +	 * be before or after the moov atom.
>  	 */
>  	public void decodeHeader() {
>  		try {
> @@ -282,8 +292,11 @@
>      									// soun or vide
>      									log.debug("Handler type: {}", MP4Atom
>      											.intToType(hdlr.getHandlerType()));
> -    									if (MP4Atom.intToType(hdlr.getHandlerType()) ==  
> "vide") {
> +    									String hdlrType =  
> MP4Atom.intToType(hdlr.getHandlerType());
> +    									if ("vide".equals(hdlrType)) {
>      										hasVideo = true;
> +    									} else if ("soun".equals(hdlrType)) {
> +    										hasAudio = true;
>      									}
>      									i++;
>      								}
> @@ -391,8 +404,8 @@
>      												//vector full of integers
>      												audioChunkOffsets = stco.getChunks();
>      												log.debug("Chunk count: {}",  
> audioChunkOffsets.size());
> -    												//set the first video offset
> -    												firstAudioTag = (Long) audioChunkOffsets.get(0);
> +    												//set the first audio offset
> +    												//firstAudioChunkOffset = (Long)  
> audioChunkOffsets.get(0);
>      											}
>      											//stts - has TimeSampleRecords
>      											MP4Atom stts =  
> stbl.lookup(MP4Atom.typeToInt("stts"), 0);
> @@ -404,6 +417,7 @@
>      												log.debug("Record data: Consecutive samples={}
> Duration={}", rec.getConsecutiveSamples(), rec.getSampleDuration());
>      												//if we have 1 record then all samples have the same
> duration
>      												if (records.size() > 1) {
> +    													//TODO: handle audio samples with varying durations
>      													log.warn("Audio samples have differing durations,  
> audio
> playback may fail");
>      												}
>      												audioSampleDuration = rec.getSampleDuration();
> @@ -442,7 +456,7 @@
>      											// stco - chunk offset
>      											// ctts - (composition) time to sample
>      											// stss - sync sample
> -    											// sdtp - independent and disposible samples
> +    											// sdtp - independent and disposable samples
>
>      											//stsd - has codec child
>      											MP4Atom stsd =  
> stbl.lookup(MP4Atom.typeToInt("stsd"), 0);
> @@ -479,8 +493,8 @@
>      												//if sample size is 0 then the table must be  
> checked due
>      												//to variable sample sizes
>      												log.debug("Sample size: {}", stsz.getSampleSize());
> -    												log.debug("Sample count: {}", videoSamples.size());
>      												videoSampleCount = videoSamples.size();
> +    												log.debug("Sample count: {}", videoSampleCount);
>      											}
>      											//stco - has Chunks
>      											MP4Atom stco =  
> stbl.lookup(MP4Atom.typeToInt("stco"), 0);
> @@ -490,7 +504,7 @@
>      												videoChunkOffsets = stco.getChunks();
>      												log.debug("Chunk count: {}",  
> videoChunkOffsets.size());
>      												//set the first video offset
> -    												firstVideoTag = (Long) videoChunkOffsets.get(0);
> +    												//firstVideoChunkOffset = (Long)  
> videoChunkOffsets.get(0);
>      											}									
>      											//stss - has Sync - no sync means all samples are  
> keyframes
>      											MP4Atom stss =  
> stbl.lookup(MP4Atom.typeToInt("stss"), 0);
> @@ -510,6 +524,7 @@
>      												log.debug("Record data: Consecutive samples={}
> Duration={}", rec.getConsecutiveSamples(), rec.getSampleDuration());
>      												//if we have 1 record then all samples have the same
> duration
>      												if (records.size() > 1) {
> +    													//TODO: handle video samples with varying durations
>      													log.warn("Video samples have differing durations,  
> video
> playback may fail");
>      												}
>      												videoSampleDuration = rec.getSampleDuration();
> @@ -549,7 +564,7 @@
>      					MP4Atom mdat = atom;	    				
>  	    				dataSize = mdat.getSize();
>  	    				log.debug("{}", ToStringBuilder.reflectionToString(mdat));
> -	    				mdatOffset = fis.getOffset() - mdat.getSize();
> +	    				mdatOffset = fis.getOffset() - dataSize;
>      					log.debug("File size: {} mdat size: {}", file.length(),  
> dataSize);
>      					
>      					break;
> @@ -561,7 +576,7 @@
>      			}
>  			}
>
> -			//the tag name to the offsets
> +			//add the tag name (size) to the offsets
>  			moovOffset += 8;
>  			mdatOffset += 8;
>  			log.debug("Offsets moov: {} mdat: {}", moovOffset, mdatOffset);
> @@ -599,6 +614,11 @@
>  			return channel.size();
>  		} catch (Exception e) {
>  			log.error("Error getTotalBytes", e);
> +		}
> +		if (file != null) {
> +			//just return the file size
> +			return file.length();
> +		} else {
>  			return 0;
>  		}
>  	}
> @@ -678,7 +698,7 @@
>  	/** {@inheritDoc}
>  	 */
>  	public boolean hasMoreTags() {
> -		return currentSample < videoSampleCount;
> +		return currentSample < frames.size();
>  	}
>
>      /**
> @@ -801,131 +821,128 @@
>  	 * Video - ts=0 size=5 bytes {17 02 00 00 00}
>  	 * Video - ts=0 size=2 bytes {52 01}
>  	 * Audio - ts=0 size=4 bytes {af 00 12 10}
> -	 * Audio - ts=0 size=9 bytes {af 01 20 00 00 00 00 00 0e}
> -	 * Regular packets follow - prefix video with 5 bytes {17 01 00 00  
> 00}
> +	 * Audio - ts=0 size=9 bytes {af 01 20 00 00 00 00 00 0e}
>  	 *
>  	 * Packet prefixes:
> -	 * 17 00 00 00 00 = Video config?
> +	 * 17 00 00 00 00 = Video extra data (first video packet)
>  	 * 17 01 00 00 00 = Keyframe
>  	 * 27 01 00 00 00 = Interframe
> -	 * af 00 = Audio config?
> +	 * af 00 = Audio extra data (first audio packet)
>  	 * af 01 = Audio
>  	 *
> -	 * Audio config:
> -	 * af 00 12 10 = AAC LC
> +	 * Audio extra data(s):
> +	 * af 00 12 10 06 = AAC LC
>  	 * af 00 13 90 56 e5 a5 48 00 = HE-AAC
>  	 */
>      private void createPreStreamingTags() {
>      	log.debug("Creating pre-streaming tags");
> -    	//video tag #1
> -    	ITag tag = new Tag(IoConstants.TYPE_VIDEO, 0, 2, null, 0);
> -		ByteBuffer body = ByteBuffer.allocate(tag.getBodySize());
> -		body.put(new byte[]{(byte) 0x52, (byte) 0});
> -		body.flip();
> -		tag.setBody(body);
> -
> -		//add tag
> -		firstTags.add(tag);
> -		//clear body for re-use
> -		//body.clear();
> -		
> -		//video tag #2
> -		tag = new Tag(IoConstants.TYPE_VIDEO, 0, 5, null,  
> tag.getBodySize());
> -		body = ByteBuffer.allocate(tag.getBodySize());
> -		body.put(new byte[]{(byte) 0x17, (byte) 0x02, (byte) 00, (byte) 00,
> (byte) 00});
> -		body.flip();
> -		tag.setBody(body);
> -		
> -		//add tag
> -		firstTags.add(tag);
> -		//clear body for re-use
> -		//body.clear();
> -		
> -    	//video tag #3
> -		tag = new Tag(IoConstants.TYPE_VIDEO, 0, 2, null,  
> tag.getBodySize());
> -		body = ByteBuffer.allocate(tag.getBodySize());
> -		body.put(new byte[]{(byte) 0x52, (byte) 0x01});
> -		body.flip();
> -		tag.setBody(body);				
> -
> -		//add tag
> -		firstTags.add(tag);
> -		//clear body for re-use
> -		//body.clear();
> -		
> -		//audio tag #1
> -		tag = new Tag(IoConstants.TYPE_AUDIO, 0, 4, null,  
> tag.getBodySize());
> -		body = ByteBuffer.allocate(tag.getBodySize());
> -		body.put(new byte[]{(byte) 0xaf, (byte) 00, (byte) 0x12, (byte)  
> 0x10});
> -		body.flip();
> -		tag.setBody(body);
> -
> -		//add tag
> -		firstTags.add(tag);
> -		//clear body for re-use
> -		//body.clear();
> -		
> -		//audio tag #2
> -		tag = new Tag(IoConstants.TYPE_AUDIO, 0, 9, null,  
> tag.getBodySize());
> -		body = ByteBuffer.allocate(tag.getBodySize());
> -		body.put(new byte[]{(byte) 0xaf, (byte) 0x01, (byte) 0x20, (byte)  
> 00,
> (byte) 00, (byte) 00, (byte) 00, (byte) 00, (byte) 0x0e});
> -		body.flip();
> -		tag.setBody(body);    	
> -
> -		//add tag
> -		firstTags.add(tag);
> -		//clear body for release
> -		//body.clear();
> -		
> -		//body.release();
> -    }
> +    	ITag tag = null;
> +    	ByteBuffer body = null;
> +    	
> +    	if (hasVideo) {
> +        	//video tag #1
> +        	tag = new Tag(IoConstants.TYPE_VIDEO, 0, 43, null, 0);
> +    		body = ByteBuffer.allocate(tag.getBodySize());
> +    		body.put(new byte[]{(byte) 0x17, (byte) 0, (byte) 0, (byte) 0,
> (byte) 0,
> +    		(byte) 0x01, (byte) 0x4d, (byte) 0x40, (byte) 0x33, (byte)  
> 0xff,
> (byte) 0xff, (byte) 0,
> +    		(byte) 0x17, (byte) 0x67, (byte) 0x4d, (byte) 0x40, (byte)  
> 0x33,
> (byte) 0x9a, (byte) 0x76,
> +    		(byte) 0x02, (byte) 0x80, (byte) 0x2d, (byte) 0xd0, (byte)  
> 0x80,
> (byte) 0,    (byte) 0,
> +    		(byte) 0x03, (byte) 0,    (byte) 0x80, (byte) 0,    (byte) 0,
> (byte) 0x19, (byte) 0x47,
> +    		(byte) 0x8c, (byte) 0x18, (byte) 0x9c, (byte) 0x01, (byte) 0,
> (byte) 0x04, (byte) 0x68,
> +    		(byte) 0xce, (byte) 0x3c, (byte) 0x80});
> +    		
> +    		body.flip();
> +    		tag.setBody(body);
>
> -    private int prevFrameSize = 0;
> +    		//add tag
> +    		firstTags.add(tag);
> +    	}
> +    	
> +    	if (hasAudio) {
> +    		//audio tag #1
> +    		tag = new Tag(IoConstants.TYPE_AUDIO, 0, 5, null,  
> tag.getBodySize());
> +    		body = ByteBuffer.allocate(tag.getBodySize());
> +    		body.put(new byte[]{(byte) 0xaf, (byte) 0, (byte) 0x12,  
> (byte) 0x10,
> (byte) 0x06});
> +    		body.flip();
> +    		tag.setBody(body);
> +
> +    		//add tag
> +    		firstTags.add(tag);
> +    	}
> +    }
>
>  	/**
>  	 *
>  	 */
>      public synchronized ITag readTag() {
> -		log.debug("Read tag - currentSample {}, prevFrameSize {}", new
> Object[]{currentSample, prevFrameSize});
> +		log.debug("Read tag");
>  		//empty-out the pre-streaming tags first
>  		if (!firstTags.isEmpty()) {
>  			log.debug("Returning pre-tag");
>  			// Return first tags before media data
>  			return firstTags.removeFirst();
>  		}		
> -
> -		int sampleSize = (Integer) videoSamples.get(currentSample) + 5;
> -		int ts = videoSampleDuration * currentSample;
> //Math.round((currentSample * timeScale) / videoSampleDuration);
> +		log.debug("Read tag - currentSample {}, prevFrameSize {}", new
> Object[]{currentSample, prevFrameSize});
> +		
> +		//get the current frame
> +		MP4Frame frame = frames.get(currentSample - 1);
> +		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)});		
> +		
> +		long samplePos = frame.getOffset();
> +		//long samplePos = samplePosMap.get(currentSample);
> +		log.debug("Read tag - samplePos {}", samplePos);
>  		log.debug("Sample position map: {}", samplePosMap);
> -		long samplePos = samplePosMap.get(currentSample);
> -		log.debug("Read tag - sampleSize {} ts {}", new Object[] 
> {sampleSize,
> ts});
>
> -		//once video works we will try adding audio
> -		//tag = new Tag(IoConstants.TYPE_AUDIO, ts, sampleSize, null,
> prevFrameSize);
> -		ITag tag = new Tag(IoConstants.TYPE_VIDEO, ts, sampleSize, null,
> prevFrameSize);
> -		log.debug("Read tag - body size: {}", tag.getBodySize());
> -		ByteBuffer body = ByteBuffer.allocate(tag.getBodySize());
> -		log.debug("Read tag - current pos {} sample pos {}",
> getCurrentPosition(), samplePos);
> -		//prefix is different for keyframes
> -		if (posTimeMap.containsKey(samplePos)) {
> -			log.debug("Writing keyframe prefix");
> -			body.put(PREFIX_VIDEO_KEYFRAME);
> -		} else {  			
> -			log.debug("Writing interframe prefix");
> -			body.put(PREFIX_VIDEO_FRAME);
> +		//determine frame type and packet body padding
> +		byte type = frame.getType();
> +		//assume video type
> +		int pad = 5;
> +		if (type == TYPE_AUDIO) {
> +			pad = 2;
>  		}
> +
> +		//create a byte buffer of the size of the sample
> +		java.nio.ByteBuffer data =  
> java.nio.ByteBuffer.allocate(sampleSize +
> pad);
>  		try {
> +			//prefix is different for keyframes
> +			if (type == TYPE_VIDEO) {
> +	    		if (frame.isKeyFrame()) {
> +	    			log.debug("Writing keyframe prefix");
> +	    			data.put(PREFIX_VIDEO_KEYFRAME);
> +	    		} else {  			
> +	    			log.debug("Writing interframe prefix");
> +	    			data.put(PREFIX_VIDEO_FRAME);
> +	    		}
> +	    		
> +	    		videoCount++;
> +			} else {
> +				log.debug("Writing audio prefix");
> +				data.put(PREFIX_AUDIO_FRAME);
> +				
> +				audioCount++;
> +			}
>  			//do we need to add the mdat offset to the sample position?
>  			channel.position(samplePos);
> -			channel.read(body.buf());
> +			channel.read(data);
>  		} catch (IOException e) {
> -			log.error("Error handling position", e);
> +			log.error("Error on channel position / read", e);
>  		}
> -		body.flip();
> -		tag.setBody(body);			
> -		currentSample++;			
>  		
> +		//chunk the data
> +		ByteBuffer payload = getChunkedPayload(data.array());		
> +		
> +		//create the tag
> +		ITag tag = new Tag(type, ts, payload.limit(), payload,  
> prevFrameSize);
> +		log.debug("Read tag - type: {} body size: {}", (type ==
> TYPE_AUDIO ? "Audio" : "Video"), tag.getBodySize());
> +		
> +		//increment the sample number
> +		currentSample++;			
> +		//set the frame / tag size
>  		prevFrameSize = tag.getBodySize();
>  	
>  		//log.debug("Tag: {}", tag);
> @@ -933,8 +950,7 @@
>  	}
>
>      /**
> -     * Key frames analysis may be used as a utility method so
> -	 * synchronize it.
> +     * Performs frame analysis and generates metadata for use in  
> seeking.
>  	 *
>       * @return             Keyframe metadata
>       */
> @@ -955,37 +971,82 @@
>          posTagMap = new HashMap<Long, Integer>();
>          samplePosMap = new HashMap<Integer, Long>();
>          // tag == sample
> -		int sample = 0;
> +		int sample = 1;
>  		Long pos = null;
>  		Enumeration records = videoSamplesToChunks.elements();
>  		while (records.hasMoreElements()) {
>  			MP4Atom.Record record = (MP4Atom.Record) records.nextElement();
>  			int firstChunk = record.getFirstChunk();
>  			int sampleCount = record.getSamplesPerChunk();
> -			//log.debug("First chunk: {} count:{}", firstChunk, sampleCount);
> +			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);
>      			//check to see if the sample is a keyframe
> -    			if (syncSamples.contains(sample)) {
> -    				//log.debug("Keyframe - sample: {}", sample);
> +    			boolean keyframe = syncSamples.contains(sample);
> +    			if (keyframe) {
> +    				log.debug("Keyframe - sample: {}", sample);
>      				positionList.add(pos);
> -    				//need to calculate ts
> -    				Integer ts = ((int) videoSampleDuration * (sample));
>      				//log.debug("Keyframe - timestamp: {}", ts);
>      				timestampList.add(ts);
>      			}
> -    			pos = pos + (Integer) videoSamples.get(sample);
> +    			int size = ((Integer) videoSamples.get(sample -  
> 1)).intValue();
> +
> +    			//create a frame
> +    			MP4Frame frame = new MP4Frame();
> +    			frame.setKeyFrame(keyframe);
> +    			frame.setOffset(pos);
> +    			frame.setSize(size);
> +    			frame.setTime(ts);
> +    			frame.setType(TYPE_VIDEO);
> +    			frames.add(frame);
> +    			
> +    			//inc and dec stuff
> +    			pos += size;
>      			sampleCount--;
> -    			sample++;
> +    			sample++;    			
>  			}
>  		}
>
>  		log.debug("Position Tag Map size: {}", posTagMap.size());
>  		log.debug("Keyframe position list size: {}", positionList.size());
>  		
> +		//add the audio frames / samples / chunks
> +		sample = 1;
> +		records = audioSamplesToChunks.elements();
> +		while (records.hasMoreElements()) {
> +			MP4Atom.Record record = (MP4Atom.Record) records.nextElement();
> +			int firstChunk = record.getFirstChunk();
> +			int sampleCount = record.getSamplesPerChunk();
> +			log.debug("Audio first chunk: {} count:{}", firstChunk,  
> sampleCount);
> +			pos = (Long) audioChunkOffsets.elementAt(firstChunk - 1);
> +			while (sampleCount > 0) {
> +    			//calculate ts
> +    			int ts = ((int) audioSampleDuration * sample);
> +    			//sample size
> +    			int size = ((Integer) audioSamples.get(sample -  
> 1)).intValue();
> +    			//create a frame
> +        		MP4Frame frame = new MP4Frame();
> +        		frame.setOffset(pos);
> +        		frame.setSize(size);
> +        		frame.setTime(ts);
> +        		frame.setType(TYPE_AUDIO);
> +        		frames.add(frame);
> +        		
> +    			//inc and dec stuff
> +    			pos += size;
> +    			sampleCount--;
> +    			sample++;
> +            }		
> +		}
> +
> +		//sort the frames
> +		Collections.sort(frames);
> +		
>  		keyframeMeta = new KeyFrameMeta();
>  		keyframeMeta.duration = duration;
>  		posTimeMap = new HashMap<Long, Long>();
> @@ -1005,6 +1066,40 @@
>  		return keyframeMeta;
>  	}
>
> +    /**
> +     * Handler for data chunks.
> +     *
> +     * @param payload
> +     * @return
> +     */
> +    private ByteBuffer getChunkedPayload(byte[] payload) {
> +    	log.debug("Get chunked payload");
> +    	int len = payload.length;
> +    	log.debug("Payload length: {}", len);
> +    	int chunkLen = 0;
> +    	int offset = 0;
> +    	//extra bytes needed for chunk markers and such
> +    	int extra = Math.max(0, Math.round(len / 4096));
> +    	//size the return array as good as possible to prevent resize
> +    	ByteBuffer ret = ByteBuffer.allocate(len + extra);
> +    	//allow resize
> +    	ret.setAutoExpand(true);
> +    	while (len > 0) {
> +        	chunkLen = Math.min(len, 4096);
> +        	log.debug("Chunk len: {}", chunkLen);
> +        	ret.put(payload, offset, chunkLen);
> +        	log.debug("read: {}", ret.position());
> +        	offset += chunkLen;
> +        	len -= chunkLen;
> +        	log.debug("len: {}", len);
> +        	if (len > 0) {
> +        		ret.put(CHUNK_MARKER);
> +        	}
> +    	}
> +    	ret.flip();
> +    	return ret;
> +    }
> +
>  	/**
>  	 * Put the current position to pos.
>  	 * The caller must ensure the pos is a valid one
>
> Modified: java/server/branches/paulg_mp4/src/org/red5/server/api/ 
> Red5.java
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- java/server/branches/paulg_mp4/src/org/red5/server/api/Red5.java	
> (original)
> +++ java/server/branches/paulg_mp4/src/org/red5/server/api/Red5.java	 
> Mon
> Sep 15 23:42:07 2008
> @@ -59,7 +59,12 @@
>      /**
>       * Current server version with revision
>       */
> -    public static final String VERSION = "Red5 Server 0.7.1-dev  
> $Revision:
> 3020 $";
> +    public static final String VERSION = "Red5 Server 0.7.2-dev  
> $Revision:
> 3020 $";
> +
> +    /**
> +     * Current server version for fmsVer requests
> +     */
> +    public static final String FMS_VERSION = "RED5/0,7,2,0";
>
>      /**
>       * Server start time
> @@ -152,6 +157,15 @@
>  	 */
>  	public static String getVersion() {
>  	    return VERSION;
> +	}
> +	
> +	/**
> +	 * Returns the current version for fmsVer requests
> +	 *
> +	 * @return String fms version
> +	 */
> +	public static String getFMSVersion() {
> +	    return FMS_VERSION;
>  	}
>  	
>  	/**
>
> Modified:
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/ 
> RTMPHandler.java
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> ---
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/ 
> RTMPHandler.java	
> (original)
> +++
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/ 
> RTMPHandler.java	
> Mon Sep 15 23:42:07 2008
> @@ -32,6 +32,7 @@
>  import org.red5.server.api.IScope;
>  import org.red5.server.api.IScopeHandler;
>  import org.red5.server.api.IServer;
> +import org.red5.server.api.Red5;
>  import org.red5.server.api.ScopeUtils;
>  import org.red5.server.api.IConnection.Encoding;
>  import org.red5.server.api.service.IPendingServiceCall;
> @@ -306,7 +307,7 @@
>  											IPendingServiceCall pc = (IPendingServiceCall) call;
>  											//send fmsver and capabilities
>  									    	StatusObject result = getStatus(NC_CONNECT_SUCCESS);
> -									    	result.setAdditional("fmsVer", "RED5/0,7,1,0");
> +									    	result.setAdditional("fmsVer", Red5.getFMSVersion());
>  											result.setAdditional("capabilities", Integer.valueOf(31));
>  									    	pc.setResult(result);
>  										}
>
> Modified:
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/ 
> FLVData.java
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> ---
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/ 
> FLVData.java	
> (original)
> +++
> java/server/branches/paulg_mp4/src/org/red5/server/net/rtmp/event/ 
> FLVData.java	
> Mon Sep 15 23:42:07 2008
> @@ -37,7 +37,7 @@
>  	 * soundSize (byte & 0x02) == 1 0: 8-bit, 2: 16-bit
>  	 * soundRate (byte & 0x0C) == 2 0: 5.5kHz, 1: 11kHz, 2: 22kHz, 3:  
> 44kHz
>  	 * soundFormat (byte & 0xf0) == 4 0: Uncompressed, 1: ADPCM, 2: MP3,
> -	 *     5: Nellymoser 8kHz mono, 6: Nellymoser
> +	 *     5: Nellymoser 8kHz mono, 6: Nellymoser, 11: Speex
>  	 */
>
>  	/*
> @@ -53,7 +53,7 @@
>  	/**
>       * Getter for disposable state
>       *
> -     * @return  <code>true</code> if FVL data is disposable,
> <code>false</code> otherwise
> +     * @return  <code>true</code> if FLV data is disposable,
> <code>false</code> otherwise
>       */
>      public boolean isDisposable() {
>  		return false;
> @@ -116,6 +116,10 @@
>       */
>  	public static final int AUDIO_NELLYMOOSER = 6;
>      /**
> +     * Speex encoded data
> +     */
> +	public static final int AUDIO_SPEEX = 11;
> +	/**
>       * Sound size when 8 khz quality marker
>       */
>  	public static final int SOUND_SIZE_8_BIT = 0;
>
> 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	
> Mon Sep 15 23:42:07 2008
> @@ -22,10 +22,13 @@
>  import java.io.IOException;
>  import java.io.ObjectInput;
>  import java.io.ObjectOutput;
> +
>  import org.apache.mina.common.ByteBuffer;
>  import org.red5.io.IoConstants;
>  import org.red5.server.api.stream.IStreamPacket;
>  import org.red5.server.stream.IStreamData;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
>
>  /**
>   * Video data event
> @@ -33,7 +36,10 @@
>  public class VideoData extends BaseEvent implements IoConstants,
> IStreamData, IStreamPacket {
>
>  	private static final long serialVersionUID = 5538859593815804830L;
> -    /**
> +
> +    private static Logger log =  
> LoggerFactory.getLogger(VideoData.class);
> +
> +	/**
>       * Videoframe type
>       */
>      public static enum FrameType {
> @@ -65,15 +71,20 @@
>  		if (data != null && data.limit() > 0) {
>  			int oldPos = data.position();
>  			int firstByte = (data.get()) & 0xff;
> +			log.debug("old pos: {} first byte: {}", oldPos, firstByte);
>  			data.position(oldPos);
>  			int frameType = (firstByte & MASK_VIDEO_FRAMETYPE) >> 4;
>  			if (frameType == FLAG_FRAMETYPE_KEYFRAME) {
> +				log.debug("Frame type = Keyframe");
>  				this.frameType = FrameType.KEYFRAME;
>  			} else if (frameType == FLAG_FRAMETYPE_INTERFRAME) {
> +				log.debug("Frame type = Interframe");
>  				this.frameType = FrameType.INTERFRAME;
>  			} else if (frameType == FLAG_FRAMETYPE_DISPOSABLE) {
> +				log.debug("Frame type = Disposable interframe");
>  				this.frameType = FrameType.DISPOSABLE_INTERFRAME;
>  			} else {
> +				log.debug("Frame type = Unknown");
>  				this.frameType = FrameType.UNKNOWN;
>  			}
>  		}
>
> 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	
> Mon Sep 15 23:42:07 2008
> @@ -889,9 +889,11 @@
>  			}
>  		}
>  		lastMessage = message.getBody();
> -		if (lastMessage instanceof IStreamData) {
> -			bytesSent += ((IStreamData) lastMessage).getData().limit();
> -		}
> +		//XXX Paul: bytesSent is updated in the doPushMessage() so I  
> assume we
> dont
> +		//also want to do it here?
> +		//if (lastMessage instanceof IStreamData) {
> +		//	bytesSent += ((IStreamData) lastMessage).getData().limit();
> +		//}
>  		doPushMessage(message);
>  	}
>
>
> Added:
> java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/ 
> MP4FrameTest.java
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- (empty file)
> +++
> java/server/branches/paulg_mp4/test/org/red5/server/io/mp4/ 
> MP4FrameTest.java	
> Mon Sep 15 23:42:07 2008
> @@ -0,0 +1,60 @@
> +package org.red5.server.io.mp4;
> +
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.List;
> +
> +import junit.framework.TestCase;
> +
> +import org.junit.Test;
> +import org.red5.io.mp4.MP4Frame;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +
> +public class MP4FrameTest extends TestCase {
> +
> +	private static Logger log =  
> LoggerFactory.getLogger(MP4FrameTest.class);
> +
> +	@Test
> +	public void testSort() {
> +		
> +		List<MP4Frame> frames = new ArrayList<MP4Frame>(6);
> +		
> +		//create some frames
> +		MP4Frame frame1 = new MP4Frame();
> +		frame1.setTime(1);
> +		frames.add(frame1);
> +		
> +		MP4Frame frame2 = new MP4Frame();
> +		frame2.setTime(6);
> +		frames.add(frame2);
> +		
> +		MP4Frame frame3 = new MP4Frame();
> +		frame3.setTime(660);
> +		frames.add(frame3);
> +		
> +		MP4Frame frame4 = new MP4Frame();
> +		frame4.setTime(3);
> +		frames.add(frame4);
> +		
> +		MP4Frame frame5 = new MP4Frame();
> +		frame5.setTime(400);
> +		frames.add(frame5);
> +		
> +		System.out.printf("Frame 1 - time: %d (should be 660)\n",
> frames.get(2).getTime());
> +
> +		Collections.sort(frames);
> +
> +		System.out.println("After sorting");
> +		
> +		int f = 1;
> +		for (MP4Frame frame : frames) {
> +			System.out.printf("Frame %d - time: %d\n", f++, frame.getTime());
> +		}
> +		
> +	}
> +
> +	
> +	
> +}
>
> _______________________________________________
> Red5devs mailing list
> Red5devs at osflash.org
> http://osflash.org/mailman/listinfo/red5devs_osflash.org




More information about the Red5devs mailing list