Author: andre
Date: 2010-03-25 14:09:34 +0100 (Thu, 25 Mar 2010)
New Revision: 41604

Added:
   
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterAnalyzer.java
   
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterTranscoder.java
Log:
added transcoder and segmenter to be able to segment ts streams to serve them 
over a cellular network

Added: 
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterAnalyzer.java
===================================================================
--- 
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterAnalyzer.java
                         (rev 0)
+++ 
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterAnalyzer.java
 2010-03-25 13:09:34 UTC (rev 41604)
@@ -0,0 +1,152 @@
+/*
+
+This file is part of the MMBase Streams application, 
+which is part of MMBase - an open source content management system.
+    Copyright (C) 2009 André van Toly, Michiel Meeuwissen
+
+MMBase Streams is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+MMBase Streams 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with MMBase. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.mmbase.streams.transcoders;
+
+import java.util.regex.*;
+import java.util.*;
+import java.io.*;
+
+import org.mmbase.servlet.FileServlet;
+import org.mmbase.bridge.*;
+
+import org.mmbase.util.logging.*;
+
+
+/**
+ * Analyzes <code>segmenter</code> output during its job, changes url field to 
m3u8 index file when 
+ * ready and rewrites m3u8 to removed full paths.
+ * 
+ * @author André van Toly
+ * @version $Id: SegmenterAnalyzer.java 40036 2009-11-30 20:27:39Z andre $
+ */
+public class SegmenterAnalyzer implements Analyzer {
+
+    private static final Logger LOG = 
Logging.getLoggerInstance(SegmenterAnalyzer.class);
+
+    public int getMaxLines() {
+        return Integer.MAX_VALUE;
+    }
+    
+    // TODO: progress matcher
+    // private static final Pattern PROGRESS = Pattern.compile("\\s*(.*?) 
audio: ([0-9]+)kbps video: ([0-9]+)kbps, time remaining: .*");
+
+    private ChainedLogger log = new ChainedLogger(LOG);
+
+    private AnalyzerUtils util = new AnalyzerUtils(log);
+
+    private List<Throwable> errors =new ArrayList<Throwable>();
+
+    public void addThrowable(Throwable t) {
+        errors.add(t);
+    }
+
+    public void addLogger(Logger logger) {
+        log.addLogger(logger);
+    }
+
+    public void analyze(String l, Node source, Node des) {
+        synchronized(util) {
+            Cloud cloud = source.getCloud();
+            
+            if (util.duration(l, source, des)) {
+                return;
+            }
+    
+            if (util.dimensions(l, source, des)) {
+                return;
+            }
+    
+            if (util.audio(l, source, des)) {
+                return;
+            }
+
+            // TODO: progress matcher
+            /*
+            {
+                Matcher m = PROGRESS.matcher(l);
+                if (m.matches()) {
+                    long pos = util.getLength(m.group(1));
+                    long audioBitrate = Integer.parseInt(m.group(2));
+                    long videoBitrate = Integer.parseInt(m.group(3));
+                    bits += ((double) (audioBitrate + videoBitrate)) * 
((double) pos - prevPos) * 1000;
+                    //System.out.println("" + pos + "ms "  + (audioBitrate + 
videoBitrate) + " -> " + (bits / pos) + " " + (100 * pos / length) + " %");
+    
+                    prevPos = pos;
+                }
+            }
+            */
+        }
+    }
+
+    public void ready(Node sourceNode, Node destNode) {
+        synchronized(util) {
+            String url = destNode.getStringValue("url");
+            url = url.substring(0, url.lastIndexOf('.')) + ".m3u8";
+            destNode.setStringValue("url", url);
+            
+            if (FileServlet.getInstance() != null) {
+                
+                String filesDirectory = FileServlet.getDirectory().toString();
+                if (!filesDirectory.endsWith("/")) {
+                    filesDirectory = filesDirectory + "/";
+                }
+                File index = new File(filesDirectory + url);
+                File temp  = new File(filesDirectory + url + ".tmp");
+                
+                try {
+                    BufferedReader in = new BufferedReader(new 
FileReader(index));
+                    PrintWriter pw = new PrintWriter(new FileWriter(temp));
+                    
+                    String line = null;
+                    while((line = in.readLine()) != null) {
+                        if (line.indexOf(filesDirectory) > -1) {
+                            line = line.replace(filesDirectory, "");
+                        }
+                        pw.println(line);
+                    }
+                    in.close();
+                    pw.close();
+
+                    // Delete original and rename new one
+                    if (!index.delete()) log.error("Could not delete file: " + 
index.toString());
+                    if (!temp.renameTo(index)) log.error("Could not rename 
file to: " + index.toString());
+                    
+                    LOG.debug("Rewrote m3u8 indexfile: " + index);
+                } catch (java.io.IOException ioe) {
+                    LOG.error("Could not rewrite m3u8 indexfile: " + ioe);
+                }
+                
+            }
+        }
+
+    }
+
+    public SegmenterAnalyzer clone() {
+        try {
+            return (SegmenterAnalyzer) super.clone();
+        } catch (CloneNotSupportedException cnfe) {
+            // doesn't happen
+            return null;
+        }
+    }
+
+}

Added: 
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterTranscoder.java
===================================================================
--- 
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterTranscoder.java
                               (rev 0)
+++ 
mmbase/branches/MMBase-1_9/applications/streams/src/main/java/org/mmbase/streams/transcoders/SegmenterTranscoder.java
       2010-03-25 13:09:34 UTC (rev 41604)
@@ -0,0 +1,144 @@
+/*
+
+This file is part of the MMBase Streams application, 
+which is part of MMBase - an open source content management system.
+    Copyright (C) 2009 André van Toly, Michiel Meeuwissen
+
+MMBase Streams is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+MMBase Streams 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with MMBase. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.mmbase.streams.transcoders;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import org.mmbase.module.core.MMBaseContext;
+import org.mmbase.bridge.*;
+import org.mmbase.bridge.util.*;
+import org.mmbase.util.logging.*;
+import org.mmbase.servlet.FileServlet;
+
+import org.mmbase.applications.media.Codec;
+import org.mmbase.applications.media.Format;
+
+
+/**
+ * The transcoder that uses <code>segmenter</code> to create segments of a 
stream including their
+ * m3u8 index file to be distributed over a cellular network. 
+ * The source of the segmenter can be found: 
http://svn.assembla.com/svn/legend/segmenter/
+ * It accepts the following arguments:
+ *   segmenter sample_low.ts 10 sample_low sample_low.m3u8 
http://www.openimages.eu/
+ * The input file, output prefix and index prefix arguments are automatically 
filled,  
+ * specify segment duration (default 10 sec.) and httpPrefix (hostname) in 
'createcaches.xml'.
+ *
+ * @author André van Toly
+ * @version $Id: SegmenterTranscoder.java 41564 2010-03-22 19:42:15Z andre $
+ */
+...@settings({"duration", "httpPrefix"})
+public class SegmenterTranscoder extends CommandTranscoder {
+
+    private static final Logger log = 
Logging.getLoggerInstance(SegmenterTranscoder.class);
+
+
+    @Override
+    protected LoggerWriter getErrorWriter(Logger log) {
+        // ffmpeg write also non-errors to stderr, so lets not log on ERROR, 
but on SERVICE.
+        // also pluging an error-detector here.
+        return new LoggerWriter(new ChainedLogger(log, new 
ErrorDetector(Pattern.compile("\\s*Unknown encoder.*"))), Level.SERVICE);
+    }
+
+    int duration = 10;
+    String httpPrefix = "http://localhost:8080/";;
+
+    public SegmenterTranscoder() {
+        format = Format.TS;
+        codec  = Codec.H264;
+    }
+    
+    public void setDuration(int d) {
+        duration = d;
+    }
+
+    public void setHttpPrefix(String h) {
+        httpPrefix = h;
+    }
+
+    @Override
+    protected  String getCommand() {
+        return "segmenter";
+    }
+
+    @Override
+    protected String[] getArguments() {
+        if (! in.getScheme().equals("file")) throw new 
UnsupportedOperationException();
+        if (! out.getScheme().equals("file")) throw new 
UnsupportedOperationException();
+
+        File inFile = new File(in.getPath());
+        File outFile = new File(out.getPath());
+        
+        //String filesDirectory = FileServlet.getDirectory().toString();
+        String filesPath = FileServlet.getBasePath("files");
+        if (filesPath.startsWith("/")) {
+            filesPath = filesPath.substring(1);
+        }
+        
+        String outStr = outFile.toString();
+        String file_prefix = outStr.substring(0, outStr.lastIndexOf('.'));
+        String index_file  = file_prefix + ".m3u8";
+
+        if (log.isDebugEnabled()) {
+            log.debug("filesPath: " + filesPath);
+            log.debug("file_prefix: " + file_prefix);
+            log.debug("index_file:  " + index_file);
+        }
+        
+        List<String> args = new ArrayList<String>();
+        
+        args.add(inFile.toString());
+        args.add("" + duration);
+        args.add(file_prefix);
+        args.add(index_file);
+        args.add(httpPrefix + filesPath);
+        
+        //args.add(out);
+
+        return args.toArray(new String[args.size()]);
+    }
+
+    private static final Pattern PROGRESS = Pattern.compile(".*time 
remaining.*");
+
+    @Override
+    protected LoggerWriter getOutputWriter(final Logger log) {
+        LoggerWriter w = new LoggerWriter(log, Level.SERVICE) {
+                @Override
+                public Level getLevel(String line) {
+                    if (PROGRESS.matcher(line).matches()) {
+                        return Level.DEBUG;
+                    }
+                    return null;
+                }
+            };
+
+        return w;
+    }
+
+    @Override
+    public SegmenterTranscoder clone() {
+        return (SegmenterTranscoder) super.clone();
+    }
+
+}

_______________________________________________
Cvs mailing list
Cvs@lists.mmbase.org
http://lists.mmbase.org/mailman/listinfo/cvs

Reply via email to