I copied over the files from the zip and i get this

compile:
    [echo] Javac version: 1.5
    [echo] Compiler adapter name: javac1.5
   [javac] Compiling 2 source files to /Volumes/FIREWIRE/www/Red5/bin
[javac] /Volumes/FIREWIRE/www/Red5/src/org/red5/io/flv/impl/FLV2.java:50: class FLV is public, should be declared in a file named FLV.java
   [javac] public class FLV implements IFLV {
   [javac]        ^
[javac] /Volumes/FIREWIRE/www/Red5/src/org/red5/io/flv/impl/FLVReader2.java:51: class FLVReader is public, should be declared in a file named FLVReader.java
   [javac] public class FLVReader implements IoConstants, ITagReader,
   [javac]        ^
   [javac] 2 errors


Martin Schipper wrote:
Hi guys,

First of all I like to say I don't read all the posts here. So maybe someone has already posted a patch... If not; here is one :)

Earlier I posted a problem I encountered while serving 'large' FLV files and files with the same filenames in different paths. The problem is the cache mechanism and the new ByteBuffer implementation. As I wrote then, maybe it's just not a good idea to 'cache' the whole FLV in heap mem... maybe it's an idea to leave the caching to the OS (unless it's a small and often played file, like a banner ;)).

To fix the problems I patched the FLV and FLVReader class so it doesn't cache anymore and it doesn't 'allocate' the MINA ByteBuffer. Furthermore I added a file-based caching mechanism for the KeyFrame Meta-info (FLVReader) so it doesn't 'scan' the whole file every time.

The patch is tested by playing a 300MB FLV via a bandwidth capped NFS share, it plays well and it uses only 7MB from the 16MB heap (-Xmx=32m and -Xms=16m).

Since you guys did all the great work I whould like to share the patch and hope you can use it (or at least the idea).

Greetz,

Martin Schipper

p.s. I patched upon SVN version 1535
p.s.2 Maybe attachments don't work in this list so I'll also put them online at: http://www.dbcorp.nl/red5/
------------------------------------------------------------------------

Index: src/org/red5/io/flv/impl/FLV.java
===================================================================
--- src/org/red5/io/flv/impl/FLV.java   (revision 1535)
+++ src/org/red5/io/flv/impl/FLV.java   (working copy)
@@ -2,21 +2,21 @@
/*
  * RED5 Open Source Flash Server - http://www.osflash.org/red5
- * + *
  * Copyright (c) 2006 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 + *
+ * 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 + *
+ * 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
  */
import java.io.File;
@@ -41,18 +41,19 @@
/**
  * A FLVImpl implements the FLV api
- * + *
  * @author The Red5 Project ([email protected])
  * @author Dominick Accattato ([EMAIL PROTECTED])
  * @author Luke Hubbard, Codegent Ltd ([EMAIL PROTECTED])
  * @author Paul Gregoire, ([EMAIL PROTECTED])
+ * @author Martin Schipper ([EMAIL PROTECTED])
  */
 public class FLV implements IFLV {
protected static Log log = LogFactory.getLog(FLV.class.getName()); - private static ICacheStore cache;
-       
+       private static ICacheStore cache;
+
        private File file;
private boolean generateMetadata;
@@ -60,12 +61,12 @@
        private IMetaService metaService;
/** - * Default constructor, used by Spring so that parameters + * Default constructor, used by Spring so that parameters
         * may be injected.
         */
        public FLV() {
        }
-       
+
        public FLV(File file) {
                this(file, false);
        }
@@ -77,16 +78,16 @@
/**
         * Sets the cache implementation to be used.
- * + *
         * @param cache
         */
        public void setCache(ICacheStore cache) {
                FLV.cache = cache;
        }
-       
+
        /*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#hasMetaData()
         */
        public boolean hasMetaData() {
@@ -96,7 +97,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#getMetaData()
         */
        public IMetaData getMetaData() throws FileNotFoundException {
@@ -106,7 +107,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#hasKeyFrameData()
         */
        public boolean hasKeyFrameData() {
@@ -116,7 +117,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#setKeyFrameData(java.util.Map)
         */
        public void setKeyFrameData(Map keyframedata) {
@@ -124,7 +125,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#getKeyFrameData()
         */
        public Map getKeyFrameData() {
@@ -134,7 +135,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#refreshHeaders()
         */
        public void refreshHeaders() throws IOException {
@@ -144,7 +145,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#flushHeaders()
         */
        public void flushHeaders() throws IOException {
@@ -154,25 +155,36 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#reader()
         */
        public ITagReader getReader() throws IOException {
                FLVReader reader = null;
-               ByteBuffer fileData = null;
-               String fileName = file.getName();
+               //ByteBuffer fileData = null;
+               //String fileName = file.getName();
- ICacheable ic = cache.get(fileName);
+               /** TODO: YYY: I disabled the cache because it consumes all our 
heap mem.
+                * Besides; it doesn't work (filename/pathname) and I run Linux 
and load
+                * the FLV's from an NFS share, Linux caches the file itself.
+                * Maybe it really gains performance if it is used for small 
files which
+                * are served very often (like banners).
+                */
+               //ICacheable ic = cache.get(fileName);
+               // look in the cache before reading the file from the disk
+               //if (null == ic || (null == ic.getByteBuffer())) {
- // look in the cache before reading the file from the disk
-               if (null == ic || (null == ic.getByteBuffer())) {
                        if (file.exists()) {
                                if (log.isDebugEnabled()) {
                                        log.debug("File size: " + 
file.length());
                                }
-                               reader = new FLVReader(new 
FileInputStream(file), generateMetadata);
+                               reader = new FLVReader(new 
FileInputStream(file), generateMetadata, file);
+
+
+                               /* YYY: Disabled.
+                                *
                                // get a ref to the mapped byte buffer
                                fileData = reader.getFileData();
+
                                // offer the uncached file to the cache
                                if (cache.offer(fileName, new 
CacheableImpl(fileData))) {
                                        if (log.isDebugEnabled()) {
@@ -183,20 +195,24 @@
                                                log.debug("Item will not be cached: 
" + fileName);
                                        }
                                }
+                                */
+
                        } else {
                                log.info("Creating new file: " + file);
                                file.createNewFile();
                        }
+               /* YYY: Disabled.
                } else {
                        fileData = ByteBuffer.wrap(ic.getBytes());
                        reader = new FLVReader(fileData, generateMetadata);
                }
+                */
                return reader;
        }
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#readerFromNearestKeyFrame(int)
         */
        public ITagReader readerFromNearestKeyFrame(int seekPoint) {
@@ -206,7 +222,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#writer()
         */
        public ITagWriter getWriter() throws IOException {
@@ -246,7 +262,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.FLV#writerFromNearestKeyFrame(int)
         */
        public ITagWriter writerFromNearestKeyFrame(int seekPoint) {
Index: src/org/red5/io/flv/impl/FLVReader.java
===================================================================
--- src/org/red5/io/flv/impl/FLVReader.java     (revision 1535)
+++ src/org/red5/io/flv/impl/FLVReader.java     (working copy)
@@ -2,24 +2,28 @@
/*
  * RED5 Open Source Flash Server - http://www.osflash.org/red5
- * + *
  * Copyright (c) 2006 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 + *
+ * 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 + *
+ * 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
  */
+import java.io.File;
 import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectOutputStream;
 import java.io.IOException;
 import java.nio.ByteOrder;
 import java.nio.MappedByteBuffer;
@@ -47,12 +51,16 @@
  * @author Dominick Accattato ([EMAIL PROTECTED])
  * @author Luke Hubbard, Codegent Ltd ([EMAIL PROTECTED])
  * @author Paul Gregoire, ([EMAIL PROTECTED])
+ * @author Martin Schipper ([EMAIL PROTECTED])
  */
 public class FLVReader implements IoConstants, ITagReader,
                IKeyFrameDataAnalyzer {
private static Log log = LogFactory.getLog(FLVReader.class.getName()); + /** TODO: YYY: added file ref for 'keyframeMeta-cache' */
+       private File file = null;
+
        private FileInputStream fis;
private FileChannel channel;
@@ -86,14 +94,78 @@
/** Buffer type / style to use **/
        private static String bufferType = "auto"; //Default
-       
+
        FLVReader() {}
-       
+
        public FLVReader(FileInputStream f) {
                this(f, false);
        }
+ /**
+        * TODO: YYY: Added an extra constructor so I have the file reference.
+        */
+       public FLVReader(FileInputStream f, boolean generateMetadata, File 
file_) {
+               file = file_;
+               boolean dontNeedToSave = this.loadKeyFramesCache();
+               this.init(f, generateMetadata);
+               if (!dontNeedToSave) this.saveKeyFramesCache();
+       }
+
+       /**
+        * TODO: YYY: load and save the 'keyframeMeta' so we don't have to scan
+        * the whole file everytime we load it.
+        * TODO: YYY: add a 'file-version' option so we are sure to have the 
correct
+        * 'keyframeMeta' for the given file.
+        */
+       public synchronized boolean loadKeyFramesCache() {
+               if (file == null) return false;
+               log.debug("Check for KeyFramesCache");
+               try {
+                       File kfCacheFile = new File(file.getPath()+".kfcache");
+                       if (kfCacheFile.isFile() && kfCacheFile.canRead()) {
+                               ObjectInputStream in = new 
ObjectInputStream(new FileInputStream(kfCacheFile));
+                               //keyframeMeta = (KeyFrameMeta) in.readObject();
+                               HashMap cacheData = (HashMap) in.readObject();
+                               keyframeMeta = (KeyFrameMeta) 
cacheData.get("keyframeMeta");
+                               posTimeMap = (HashMap<Long, Long>) 
cacheData.get("posTimeMap");
+                               posTagMap = (HashMap<Long, Integer>) 
cacheData.get("posTagMap");
+                               in.close();
+                               log.debug("Loaded KeyFramesCache for file 
"+file.getPath());
+                               return true;
+                       }
+               } catch (Exception e) { e.printStackTrace(); /* ignore */ }
+               return false;
+       }
+       public boolean saveKeyFramesCache() {
+               if (file == null) return false;
+               log.debug("Save KeyFramesCache");
+               try {
+                       File kfCacheFile = new 
File(file.getPath()+".Red5cache");
+                       if (kfCacheFile.isFile() && kfCacheFile.canRead()) {
+                               log.debug("already know this file");
+                               return true;
+                       }
+                       if (kfCacheFile.createNewFile() && 
kfCacheFile.canWrite()) {
+                               ObjectOutputStream out = new 
ObjectOutputStream(new FileOutputStream(kfCacheFile));
+                               HashMap cacheData = new HashMap();
+                               cacheData.put("keyframeMeta", keyframeMeta);
+                               cacheData.put("posTimeMap", posTimeMap);
+                               cacheData.put("posTagMap", posTagMap);
+                               out.writeObject(cacheData);
+                               out.close();
+                               log.debug("Saved KeyFramesCache for file 
"+file.getPath());
+                               return true;
+                       }
+               } catch (Exception e) { e.printStackTrace(); /* ignore */ }
+               return false;
+       }
+
+       /** TODO: YYY: renamed the constructor to init */
        public FLVReader(FileInputStream f, boolean generateMetadata) {
+               this.init(f,generateMetadata);
+       }
+
+       public void init (FileInputStream f, boolean generateMetadata) {
                this.fis = f;
                this.generateMetadata = generateMetadata;
                channel = fis.getChannel();
@@ -102,9 +174,13 @@
                                        .size());
                        mappedFile.order(ByteOrder.BIG_ENDIAN);
                        if (log.isDebugEnabled()) {
-                               log.debug("Mapped file capacity: " + 
mappedFile.capacity() + " Channel size: " + channel.size());
+                               log.debug("Mapped file capacity: " + mappedFile.capacity() + " 
Channel size: " + channel.size() + " remaining: " + mappedFile.remaining());
                        }
-                       switch (bufferType.hashCode()) {
+
+                       /** TODO: YYY: Don't allocate a fixed bytebuffer, it 
consumes all our heap mem.
+                        * I serve 300MB flv's, it just won't work for me..
+                        */
+                       /*switch (bufferType.hashCode()) {
                                case 3198444: //heap
                                        //Get a heap buffer from buffer pool
                                        in = 
ByteBuffer.allocate(mappedFile.capacity(), false);
@@ -117,13 +193,17 @@
                                default:
                                        //Let MINA choose
                                        in = 
ByteBuffer.allocate(mappedFile.capacity());
-                       }
+                       }*/
                        //drop in the file
-                       in.put(mappedFile);
+                       //in.put(mappedFile);
                        //prepare the buffer for access
-                       in.flip();
+                       //in.flip();
+
+                       /** TODO: YYY: Just wrap the NIO ByteBuffer in a nice 
MINA bytebuffer. */
+                       in = ByteBuffer.wrap(mappedFile);
+
                        if (log.isDebugEnabled()) {
-                               log.debug("Direct buffer: " + in.isDirect() + " Read only: 
" + in.isReadOnly() + " Pooled: " + in.isPooled());
+                               log.debug("Buffer limit: " + in.limit() + "Direct buffer: " + 
in.isDirect() + " Read only: " + in.isReadOnly() + " Pooled: " + in.isPooled());
                        }
                } catch (IOException e) {
                        log.error("FLVReader :: FLVReader ::>\n", e);
@@ -142,7 +222,7 @@
/**
         * Accepts mapped file bytes to construct internal members.
- * + *
         * @param mappedFile
         * @param generateMetadata
         */
@@ -169,7 +249,7 @@
/**
         * Returns the file buffer.
- * + *
         * @return file bytes
         */
        public ByteBuffer getFileData() {
@@ -191,7 +271,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.Reader#getFLV()
         */
        public IStreamableFile getFile() {
@@ -201,7 +281,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.Reader#getOffset()
         */
        public int getOffset() {
@@ -211,7 +291,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.Reader#getBytesRead()
         */
        synchronized public long getBytesRead() {
@@ -224,10 +304,11 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.Reader#hasMoreTags()
         */
        synchronized public boolean hasMoreTags() {
+               //log.debug("hasMoreTags : remaining : "+in.remaining());
                return in.remaining() > 4;
        }
@@ -271,7 +352,7 @@ /*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.Reader#readTag()
         */
        synchronized public ITag readTag() {
@@ -291,7 +372,7 @@
ByteBuffer body = ByteBuffer.allocate(tag.getBodySize());
                final int limit = in.limit();
-               // XXX Paul: this assists in 'properly' handling damaged FLV 
files              
+               // XXX Paul: this assists in 'properly' handling damaged FLV 
files
                int newPosition = in.position() + tag.getBodySize();
                if (newPosition <= limit) {
                        in.limit(newPosition);
@@ -307,7 +388,7 @@
/*
         * (non-Javadoc)
- * + *
         * @see org.red5.io.flv.Reader#close()
         */
        synchronized public void close() {
@@ -335,6 +416,8 @@
                        return keyframeMeta;
                }
+ log.debug("analyzeKeyFrames");
+
                List<Integer> positionList = new ArrayList<Integer>();
                List<Integer> timestampList = new ArrayList<Integer>();
                int origPos = in.position();
@@ -391,6 +474,7 @@
keyframeMeta = new KeyFrameMeta();
                posTimeMap = new HashMap<Long, Long>();
+
                keyframeMeta.positions = new int[positionList.size()];
                keyframeMeta.timestamps = new int[timestampList.size()];
                for (int i = 0; i < keyframeMeta.positions.length; i++) {
@@ -400,6 +484,7 @@
                                        .get(i));
                }
                return keyframeMeta;
+
        }
synchronized public void position(long pos) { ------------------------------------------------------------------------

_______________________________________________
Red5 mailing list
[email protected]
http://osflash.org/mailman/listinfo/red5_osflash.org

_______________________________________________
Red5 mailing list
[email protected]
http://osflash.org/mailman/listinfo/red5_osflash.org

Reply via email to