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