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
[email protected]
http://lists.mmbase.org/mailman/listinfo/cvs