My patch disables the cache function completely. I disabled it because my server wasn't happy when he should cache multiple 300MB flv's :) I run Red5 on Linux and store my FLV's on a network share (nfs). Linux caches the files himself, so there is not that much performance loss. (Besides, I serve a (currently) 130GB archive of flv's, that's not cachable anyway).

Grtz,

Martin

Dan Rossi wrote:
Does it still allow for caching or what. When the cache reached its limit or whatever we had our trailer videos playing when it should be the larger versions, they are named the same too.

Interalab Sales wrote:
This sounds like a possible fix to my problems too. Can someone reply to let me know when/if this patch will be rolled into the trunk?

Bill

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


------------------------------------------------------------------------

_______________________________________________
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