[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