You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(2) |
Jun
|
Jul
(29) |
Aug
(20) |
Sep
(6) |
Oct
(89) |
Nov
(24) |
Dec
(12) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(16) |
Feb
(1) |
Mar
(7) |
Apr
(32) |
May
(36) |
Jun
(21) |
Jul
(65) |
Aug
(6) |
Sep
|
Oct
(30) |
Nov
(26) |
Dec
(44) |
2006 |
Jan
(35) |
Feb
(15) |
Mar
(29) |
Apr
(24) |
May
(8) |
Jun
(2) |
Jul
(7) |
Aug
(11) |
Sep
(61) |
Oct
(36) |
Nov
(10) |
Dec
(14) |
2007 |
Jan
(6) |
Feb
(43) |
Mar
(55) |
Apr
(20) |
May
(26) |
Jun
(21) |
Jul
(24) |
Aug
(22) |
Sep
(24) |
Oct
(4) |
Nov
|
Dec
(60) |
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(51) |
Jul
(2) |
Aug
(13) |
Sep
|
Oct
(16) |
Nov
(99) |
Dec
(12) |
2009 |
Jan
(23) |
Feb
(44) |
Mar
(4) |
Apr
|
May
|
Jun
(9) |
Jul
(70) |
Aug
(14) |
Sep
(13) |
Oct
(6) |
Nov
|
Dec
(3) |
2010 |
Jan
(13) |
Feb
(17) |
Mar
(2) |
Apr
(3) |
May
(71) |
Jun
(19) |
Jul
(95) |
Aug
|
Sep
|
Oct
|
Nov
(71) |
Dec
(3) |
2011 |
Jan
(52) |
Feb
|
Mar
(34) |
Apr
(10) |
May
(8) |
Jun
(8) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
|
2012 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(2) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
From: jreichen <jre...@us...> - 2010-11-03 03:42:39
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/listener In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/src/sagex/streaming/listener Added Files: AppContextListener.java Log Message: --- NEW FILE: AppContextListener.java --- package sagex.streaming.listener; import java.io.File; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.mortbay.log.Log; import sagex.api.Global; import sagex.streaming.httpls.segment.SegmentManager; import sagex.streaming.httpls.segment.SegmentProducer; import sagex.streaming.httpls.segment.SegmenterProcess; import sagex.streaming.io.StreamGobbler; public class AppContextListener implements ServletContextListener { public static final String SEGMENT_MANAGER_ATTRIBUTE_NAME = "Sage Streaming Segment Manager"; /** * Configure the location of the ffmpeg executable, which is under the WEB-INF directory. * Make sure ffmpeg has executable permissions on Linux and MacOS */ public void contextInitialized(ServletContextEvent event) { // TODO Logging System.out.println("Stdout: starting media streaming web app"); System.err.println("Stderr: starting media streaming web app"); System.out.println("Stderr class: " + System.err.getClass()); String ffmpegPath = ""; if (Global.IsLinuxOS()) { ffmpegPath = "/WEB-INF/bin/linux32/ffmpeg"; } else if (Global.IsMacOS()) { ffmpegPath = "/WEB-INF/bin/macos/ffmpeg"; } else if (Global.IsWindowsOS()) { ffmpegPath = "/WEB-INF/bin/win32/ffmpeg.exe"; } File ffmpegFile = null; try { URL ffmpegUrl = event.getServletContext().getResource(ffmpegPath); if (ffmpegUrl != null) { // jump through hoops to deal with spaces in the path (mainly for Windows "Program Files" directory) // http://weblogs.java.net/blog/2007/04/25/how-convert-javaneturl-javaiofile try { // convert %20 to a space ffmpegFile = new File(ffmpegUrl.toURI()); } catch (URISyntaxException e) { ffmpegFile = new File(ffmpegUrl.getPath()); } Log.debug("FFmpeg executable location: " + ffmpegFile.getAbsolutePath()); SegmenterProcess.setProcessLocation(ffmpegFile); } } catch (MalformedURLException e) { Log.info(e.getMessage()); Log.ignore(e); } catch (IllegalArgumentException e) { Log.info(e.getMessage()); Log.ignore(e); } setFileExecutablePermission(ffmpegFile); event.getServletContext().setAttribute(SEGMENT_MANAGER_ATTRIBUTE_NAME, new SegmentManager()); } /** * Make sure all ffmpeg processes are destroyed so they aren't left running and so * redeployments can overwrite the executable. */ public void contextDestroyed(ServletContextEvent event) { SegmentManager sm = (SegmentManager) event.getServletContext().getAttribute(AppContextListener.SEGMENT_MANAGER_ATTRIBUTE_NAME); synchronized (sm) { List<SegmentProducer> segmentProducers = sm.removeSegmentProducers(); Log.debug("Destroying " + segmentProducers.size() + " remaining ffmpeg processes."); for (SegmentProducer sp : segmentProducers) { synchronized(sp) { try { sp.stop(); } catch (Throwable t) { Log.info("Unexpected exception while cleaning up processes: " + t.getMessage()); Log.ignore(t); } } } segmentProducers.clear(); } } private void setFileExecutablePermission(File file) { if (Global.IsLinuxOS() || Global.IsMacOS()) { try { Process p = Runtime.getRuntime().exec("chmod +x " + file.getAbsolutePath()); StreamGobbler stdoutGobbler = new StreamGobbler("chmod stdout", p.getInputStream(), null, true); StreamGobbler stderrGobbler = new StreamGobbler("chmod stderr", p.getErrorStream(), null, true); ExecutorService threadPool = Executors.newFixedThreadPool(2); Future<?> stdoutFuture = threadPool.submit(stdoutGobbler); Future<?> stderrFuture = threadPool.submit(stderrGobbler); p.waitFor(); stdoutFuture.get(); stderrFuture.get(); } catch (Exception e) { Log.info(e.getMessage()); Log.ignore(e); } } } } |
From: jreichen <jre...@us...> - 2010-11-03 03:42:38
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/httpls/segment In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/src/sagex/streaming/httpls/segment Added Files: SegmentManager.java SegmenterState.java SegmentProducer.java SegmenterProcess.java Log Message: --- NEW FILE: SegmentProducer.java --- package sagex.streaming.httpls.segment; import java.io.File; import java.io.IOException; import java.io.OutputStream; import org.mortbay.log.Log; import sagex.api.MediaFileAPI; /** * Encapsulates the state of an HTTP Live Streaming stream and the SegmenterProcess that * creates that stream. * Calling code should always do so within a block that synchronizes * on this object. If it also calls SegmentManager and needs to hold locks on * both objects, it should acquire the lock on SegmentManager first to avoid deadlock. */ public class SegmentProducer { private SegmenterState state; private SegmenterProcess segmenterProcess; // private TranscoderProcess transcoderProcess; private String userAgent = ""; private long lastActivity = 0; public SegmentProducer(String conversionId, String userAgent) { this.state = new SegmenterState(conversionId); this.userAgent = userAgent; this.lastActivity = System.currentTimeMillis(); } public void reset() throws IOException { System.out.println("Resetting segment producer"); if (segmenterProcess != null) { System.out.println("Killing segmenter process"); segmenterProcess.stopProcess(); } Object mediaFile = MediaFileAPI.GetMediaFileForID(state.getMediaFileId()); int sageSegment = state.getMediaFileSegment(); File f = MediaFileAPI.GetFileForSegment(mediaFile, sageSegment); System.out.println("Creating new segmenter process"); segmenterProcess = new SegmenterProcess(); segmenterProcess.startProcess(f, userAgent, state.getQuality(), state.getSegment()); lastActivity = System.currentTimeMillis(); } // TODO private public boolean nextState(int segmentIndex, String quality, int sageObjectId, int mediaFileSegment) { boolean val = false; try { val = state.nextState(segmentIndex, quality, sageObjectId, mediaFileSegment); } finally { lastActivity = System.currentTimeMillis(); } return val; } public long getNextSegmentLength() { return segmenterProcess.getNextSegmentLength(); } public void streamNextSegment(OutputStream os) { lastActivity = System.currentTimeMillis(); try { segmenterProcess.streamNextSegment(os); } catch (Throwable t) { // Log.info(t.getMessage()); // Log.ignore(t); t.printStackTrace(System.out); this.state.reset(); } finally { lastActivity = System.currentTimeMillis(); } } public void stop() { if (segmenterProcess != null) { segmenterProcess.stopProcess(); } } public long getLastActivity() { return lastActivity; } } --- NEW FILE: SegmenterState.java --- package sagex.streaming.httpls.segment; import org.mortbay.log.Log; public class SegmenterState { private boolean isInitialState = true; private String conversionId; private int segment = -1; private String quality; // other properties private int mediaFileId; // could be airingId (which could have multiple mediaFileIds) or mediaFileId private int mediaFileSegment = 0; public SegmenterState(String conversionId) { this.conversionId = conversionId; reset(); } public void reset() { isInitialState = true; conversionId = null; segment = -1; quality = null; mediaFileId = 0; mediaFileSegment = 0; } public boolean nextState(int segment, String quality, int mediaFileId, int mediaFileSegment) { if (quality == null) { throw new IllegalArgumentException("Quality argument is null"); } System.out.println("nextState() old segment = " + this.segment + ", new segment = " + segment); System.out.println("nextState() old quality = " + this.quality + ", new quality = " + quality); boolean isNextStateSequential = (segment == this.segment + 1) && quality.equals(this.quality) && (mediaFileId == this.mediaFileId) && (mediaFileSegment == this.mediaFileSegment); this.segment = segment; this.quality = quality; this.mediaFileId = mediaFileId; this.mediaFileSegment = mediaFileSegment; // Always start new process when in the initial state. // Otherwise reuse the existing process if the next segment of the same file is requested. boolean returnVal = !isInitialState && isNextStateSequential; isInitialState = false; System.out.println("SegmenterState.nextState() returns " + returnVal); return returnVal; } public int getSegment() { return segment; } public int getMediaFileId() { return mediaFileId; } public int getMediaFileSegment() { return mediaFileSegment; } public String getQuality() { return quality; } } --- NEW FILE: SegmenterProcess.java --- package sagex.streaming.httpls.segment; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import sagex.api.MediaFileAPI; import sagex.streaming.io.SegmentInputStream; import sagex.streaming.io.StreamGobbler; import sagex.streaming.servlet.HTTPLiveStreamingPlaylistServlet; public class SegmenterProcess implements UncaughtExceptionHandler { /** * The ffmepg executable used for transcoding and segmenting audio and video. */ private static File ffmpegFile; private static final String SEGMENTER_PARAM = "segmenter"; /** * A thread pool that allows the stream gobblers to reuse threads if available */ private static ExecutorService threadPool = Executors.newCachedThreadPool(); /** * The process that converts the video and audio into the requested formats for the client */ private Process transcoderProcess = null; /** * The process that produces HTTP Live Streaming segments */ private Process segmenterProcess = null; /** * Custom input stream that reads the segmenter's specially formatted output and makes it available * to calling code as a normal input stream. */ private SegmentInputStream segmentInputStream = null; private double currentTime = 0; private StreamGobbler stdoutTranscoderGobbler = null; private StreamGobbler stderrTranscoderGobbler = null; private StreamGobbler stdoutSegmenterGobbler = null; private StreamGobbler stderrSegmenterGobbler = null; /** * Set the location of the ffmpeg executable when the web app starts */ public static void setProcessLocation(File f) { if (!f.exists()) { throw new IllegalArgumentException("File does not exist."); } System.out.println("SegmenterProcess for HTTPLS web streaming is " + f.getAbsolutePath()); ffmpegFile = f; } /** * Start transcoder and segmenter streams along with their gobblers. After this method * exits, the transcoded stream is ready to be copied to the client when it's requested. */ public void startProcess(File inputFile, String userAgent, String quality, int sequence) throws IOException { try { double startTime = sequenceToStartTime(sequence); currentTime = startTime; // get builders for the transcoding and segmenting processes ProcessBuilder transcoderProcessBuilder = getTranscodeProcessBuilder(inputFile, userAgent, quality, startTime); ProcessBuilder segmenterProcessBuilder = getSegmenterProcessBuilder(sequence); // start the transcoding and segmenting processes transcoderProcess = transcoderProcessBuilder.start(); segmenterProcess = segmenterProcessBuilder.start(); // copy the transcoder's a/v output to the segmenter // if this gobbler exits, streaming cannot continue so the streams should be closed stdoutTranscoderGobbler = new StreamGobbler("transcoder stdout", transcoderProcess.getInputStream(), segmenterProcess.getOutputStream(), true); // Log the stderr output of the transcoder and segmenter processes stderrTranscoderGobbler = new StreamGobbler("transcoder stderr", transcoderProcess.getErrorStream()); stderrSegmenterGobbler = new StreamGobbler("segmenter stderr", segmenterProcess.getErrorStream()); // start process gobblers, reusing existing threads if available threadPool.submit(stdoutTranscoderGobbler); threadPool.submit(stderrTranscoderGobbler); threadPool.submit(stderrSegmenterGobbler); segmentInputStream = new SegmentInputStream(segmenterProcess.getInputStream()); } catch (IOException e) { stopProcess(); throw e; } } public void stopProcess() { interrupt(stdoutSegmenterGobbler); interrupt(stderrSegmenterGobbler); interrupt(stdoutTranscoderGobbler); interrupt(stderrTranscoderGobbler); System.out.println("SegmenterProcess: stop transcoder process"); if (transcoderProcess != null) { try { int val = transcoderProcess.exitValue(); System.out.println("SegmenterProcess: process finished with exit value " + val); } catch (IllegalThreadStateException e) { // process has not terminated OutputStream os = transcoderProcess.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); try { // try to safely kill the process osw.append('q'); osw.flush(); osw.close(); try { Thread.interrupted(); Thread.sleep(500); } catch (InterruptedException e1) { // Log.info(e1.getMessage()); // Log.ignore(e1); e1.printStackTrace(System.out); } int val = transcoderProcess.exitValue(); System.out.println("SegmenterProcess: process stopped with 'q' command. Exit value " + val); } catch (IllegalThreadStateException e2) { destroyProcess(transcoderProcess); System.out.println("SegmenterProcess: process forcibly terminated due to IllegalThreadStateException"); } catch (IOException e3) { destroyProcess(transcoderProcess); System.out.println("SegmenterProcess: process forcibly terminated due to IOException"); } // catch (InterruptedException e4) // { // proc.destroy(); // System.out.println("SegmenterProcess: process forcibly terminated due to InterruptedException"); // } catch (Throwable t) { destroyProcess(transcoderProcess); /* TODO Log.info*/System.out.println("SegmenterProcess: process forcibly terminated due to unexpected exception: " + t.getMessage()); // Log.ignore(t); t.printStackTrace(System.out); } } } System.out.println("SegmenterProcess: stop segmenter process"); if (segmenterProcess != null) { destroyProcess(segmenterProcess); } if (segmentInputStream != null) { try { System.out.println("SegmenterProcess: close segmenter input stream"); segmentInputStream.close(); } catch (IOException e) { // ignore } } // reset variables currentTime = 0; transcoderProcess = null; segmenterProcess = null; segmentInputStream = null; stdoutTranscoderGobbler = null; stderrTranscoderGobbler = null; stdoutSegmenterGobbler = null; stderrSegmenterGobbler = null; } /** * Utility function to catch any unexpected exception and prevent calling code from exiting prematurely */ private void interrupt(StreamGobbler gobbler) { if (gobbler != null) { try { gobbler.interrupt(); } catch (Throwable t) { // Log.info(t.getMessage()); // Log.ignore(t); t.printStackTrace(System.out); } } } /** * Utility function to catch any unexpected exception and prevent calling code from exiting prematurely */ private void destroyProcess(Process p) { if (p != null) { try { p.destroy(); } catch (Throwable t) { // Log.info(t.getMessage()); // Log.ignore(t); t.printStackTrace(System.out); } } } public long getNextSegmentLength() { segmentInputStream.fillNextSegment(); return segmentInputStream.getSegmentLength(); } // TODO throw custom exception public void streamNextSegment(OutputStream os) throws Exception { currentTime += HTTPLiveStreamingPlaylistServlet.TARGET_DURATION; try { stdoutSegmenterGobbler = new StreamGobbler("segmenter stdout", segmentInputStream, os, false); // stdoutSegmenterGobbler.setUncaughtExceptionHandler(this); Future<?> stdoutFuture = threadPool.submit(stdoutSegmenterGobbler); stdoutFuture.get(); // wait for the gobbler to exit } catch (InterruptedException e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); stopProcess(); throw new Exception(e); } catch (CancellationException e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); stopProcess(); throw new Exception(e); } catch (ExecutionException e) { // StreamGobbler threw an exception in its call() method // logging is handled in StreamGobbler stopProcess(); throw new Exception(e); } finally { try { if (segmenterProcess == null) { System.out.println("segmenterProcess has been closed and set to null by a call to stopProcess"); } else { int exitValue = segmenterProcess.exitValue(); segmenterProcess = null; System.out.println("streamNextSegment segmenterProcess exited with value " + exitValue); System.out.println("Killing transcoder process"); stopProcess(); throw new Exception("SegmenterProcess: segmenter unexpectedly exited"); } } catch (IllegalThreadStateException e) { System.out.println("streamNextSegment segmenterProcess is still running"); } } } /** * Destroys the ffmpeg processes if the output gobbler encounters an exception */ public void uncaughtException(Thread arg0, Throwable arg1) { stopProcess(); } private ProcessBuilder getTranscodeProcessBuilder(File inputFile, String userAgent, String quality, double startTime) { Object mediaFile = MediaFileAPI.GetMediaFileForFilePath(inputFile); String aspect = MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Video.Aspect"); // String framerate = MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Video.FPS"); Integer numAudioStreams = Integer.parseInt(MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Audio.NumStreams")); System.out.println("numAudioStreams " + numAudioStreams); for (int i = 0; i < numAudioStreams; i++) { Integer streamIndex = Integer.parseInt(MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Audio." + i + ".Index")); System.out.println("Audio Stream " + i + ", Index = " + streamIndex); } Map<String, String[]> qualityMap = new HashMap<String, String[]>(); System.out.println("SegmenterProcess: userAgent " + userAgent); // TODO improve useragent detection, quality settings, and don't hard-code settings // TODO TN2224 for qualities, different for iPad vs iPhone/iPod touch String size = "400x300"; if (userAgent.contains("iPhone") || userAgent.contains("iPod") || userAgent.length() == 0) { // 16:9 String[] s1 = {"400x224", "1200k", "1300k", "24.0", "40k", "44100"}; String[] s2 = {"400x224", "800k", "940k", "24.0", "40k", "44100"}; String[] s3 = {"400x224", "600k", "740k", "24.0", "40k", "44100"}; String[] s4 = {"400x224", "400k", "540k", "24.0", "40k", "44100"}; String[] s5 = {"400x224", "200k", "340k", "24.0", "40k", "44100"}; String[] s6 = {"400x224", "110k", "250k", "24.0", "40k", "44100"}; qualityMap = new HashMap<String, String[]>(); qualityMap.put("1240", s1); qualityMap.put("840", s2); qualityMap.put("640", s3); qualityMap.put("440", s4); qualityMap.put("240", s5); qualityMap.put("150", s6); } else if (userAgent.contains("iPad") || userAgent.matches("Apple Mac OS X .* CoreMedia .*") || // Mac desktop Safari video element userAgent.matches("QuickTime.*") || // Windows desktop Safari video element userAgent.matches(".*AppleWebKit.* Version.* Safari.*")) // Desktop Safari browser { // 16:9 String[] s1 = {"640x360", "1200k", "1300k", "24.0", "40k", "44100"}; String[] s2 = {"640x360", "800k", "940k", "24.0", "40k", "44100"}; String[] s3 = {"640x360", "600k", "740k", "24.0", "40k", "44100"}; String[] s4 = {"400x224", "400k", "540k", "24.0", "40k", "44100"}; String[] s5 = {"400x224", "200k", "340k", "24.0", "40k", "44100"}; String[] s6 = {"400x224", "110k", "250k", "24.0", "40k", "44100"}; qualityMap = new HashMap<String, String[]>(); qualityMap.put("1240", s1); qualityMap.put("840", s2); qualityMap.put("640", s3); qualityMap.put("440", s4); qualityMap.put("240", s5); qualityMap.put("150", s6); } String[] currentQuality = qualityMap.get(quality); if (currentQuality == null) { throw new IllegalArgumentException("Invalid quality argument"); } String cmd = ffmpegFile.getAbsolutePath(); ProcessBuilder pb = new ProcessBuilder(); pb.command(cmd, // "--conversion-id", "A", // "--port-number", "46631", "-threads", Integer.toString(Runtime.getRuntime().availableProcessors()),//"4", "-flags2", "+fast", //! added 10/25/10 "-flags", "+loop", "-g", "30", "-keyint_min", "1", "-bf", "0", "-b_strategy", "0", "-flags2",// "", "-wpred-dct8x8",// "", "-cmp", "+chroma", "-deblockalpha", "0", "-deblockbeta", "0", "-refs", "1", "-coder", "0", "-me_range", "16", "-subq", "5", "-partitions", "+parti4x4+parti8x8+partp8x8", "-trellis", "0", "-sc_threshold", "40", "-i_qfactor", "0.71", "-qcomp", "0.6", // "-map", "0.0:0.0", // "-map", "0.1:0.1", // "-map", "0.1:0.0", // "-map", "0.0:0.1", "-ss", Double.toString(startTime), "-i", inputFile.getAbsolutePath(), // "-cropleft", "0", // "-cropright", "0", // "-croptop", "0", // "-cropbottom", "0", "-s", currentQuality[0], "-aspect", aspect, // "-crf", "22", //!!! "-y", "-f", "mpegts", "-async", "1", "-vcodec", "libx264", "-level", "30", "-bufsize", "512k", "-b", currentQuality[1], "-bt", currentQuality[2], "-qmax", "48", "-qmin", "2", "-r", "29.97", // "-acodec", "libmp3lame", "-acodec", "libfaac", "-ab", currentQuality[4], "-ar", currentQuality[5], "-ac", "2", "-"); return pb; } private ProcessBuilder getSegmenterProcessBuilder(long sequence) { ProcessBuilder pb = new ProcessBuilder(); pb.command(ffmpegFile.getPath(), SEGMENTER_PARAM, Integer.toString(HTTPLiveStreamingPlaylistServlet.TARGET_DURATION), Long.toString(sequence)); return pb; } private long parseSequence(String sequence) { long sequenceval = 0; if (sequence != null) { try { sequenceval = Long.parseLong(sequence); } catch (NumberFormatException e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); throw new IllegalArgumentException("Sequence parameter is not a valid number.", e); } if (sequenceval < 0) { throw new IllegalArgumentException("Sequence parameter is not a valid number."); } } return sequenceval; } private double sequenceToStartTime(long sequence) { return sequence * HTTPLiveStreamingPlaylistServlet.TARGET_DURATION; } public static void main(String[] args) throws IOException { setProcessLocation(new File("bin/linux32/ffmpeg")); File f = new File("/home/jreichen/Seinfeld-TheSoupNazi-9012674-0.mp4"); File fin = new File("/home/jreichen/Seinfeld-TheSoupNazi-9012674-0.mpegts"); File fout = new File("/home/jreichen/segmenter.mpegts"); System.out.println(ffmpegFile.getAbsolutePath()); SegmenterProcess sp = new SegmenterProcess(); ProcessBuilder pb = sp.getTranscodeProcessBuilder(f, "iPhone", "512", 0.0); FileInputStream fis = null; FileOutputStream fos = null; fout.delete(); try { fis = new FileInputStream(fin); fos = new FileOutputStream(fout); // sp.startProcess(transcodeProcess.getInputStream(), ffos, null); // sp.startProcess(fis, fos, null); } finally { fis.close(); fos.close(); } } } --- NEW FILE: SegmentManager.java --- package sagex.streaming.httpls.segment; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import org.mortbay.log.Log; /** * Keeps track of the current state for a conversion id. * Calling code should always do so within a block that synchronizes * on this object. If it also calls SegmentProducer and needs to hold locks on * both objects, it should acquire the lock on this first to avoid deadlock. */ public class SegmentManager { private Map<String, SegmentProducer> segmentProducerMap = new LinkedHashMap<String, SegmentProducer>(); // TODO determine when the last segment has been sent private int currentIndex; private Timer timer = null; public void addSegmentProducer(String conversionId, SegmentProducer segmentProducer) { if (segmentProducerMap.containsKey(conversionId)) { throw new IllegalArgumentException("Conversion id " + conversionId + " already exists"); } segmentProducerMap.put(conversionId, segmentProducer); synchronized (this) { if (timer == null) { timer = new Timer("ffmpeg cleanup timer"); timer.schedule(new CleanupTask(), 5 * 60 * 1000, 1 * 60 * 1000); } } } public SegmentProducer getSegmentProducer(String conversionId) { SegmentProducer segmentProducer = segmentProducerMap.get(conversionId); return segmentProducer; } public List<SegmentProducer> getSegmentProducers() { List<SegmentProducer> segmentProducers = new ArrayList<SegmentProducer>(); segmentProducers.addAll(segmentProducerMap.values()); return segmentProducers; } public List<SegmentProducer> removeSegmentProducers() { synchronized (this) { if (timer != null) { timer.cancel(); timer = null; } } List<SegmentProducer> segmentProducers = getSegmentProducers(); segmentProducerMap.clear(); return segmentProducers; } /** * Clean up any ffmpeg processes that have been inactive for a certain amount of time */ private class CleanupTask extends TimerTask { @Override public void run() { synchronized (SegmentManager.this) { long now = System.currentTimeMillis(); Iterator<Map.Entry<String, SegmentProducer>> mapIterator = segmentProducerMap.entrySet().iterator(); while (mapIterator.hasNext()) { Map.Entry<String, SegmentProducer> currentEntry = mapIterator.next(); SegmentProducer currentSegmentProducer = currentEntry.getValue(); synchronized (currentSegmentProducer) { System.out.println("Segment Producer has been inactive for " + (now - currentSegmentProducer.getLastActivity()) / 1000 + " seconds"); if (now - currentSegmentProducer.getLastActivity() > 5 * 60 * 1000) // 5 minutes { try { currentSegmentProducer.stop(); } catch (Throwable t) { // Log.info(t.getMessage()); // Log.ignore(t); t.printStackTrace(System.out); } System.out.println("Map size before remove: " + segmentProducerMap.size()); mapIterator.remove(); System.out.println("Map size after remove: " + segmentProducerMap.size()); } } } // if ((timer != null) && (segmentProducers.size() == 0)) if ((timer != null) && (segmentProducerMap.size() == 0)) { timer.cancel(); timer = null; } } } } } |
From: jreichen <jre...@us...> - 2010-11-03 03:42:38
|
Update of /cvsroot/sageplugins/MediaStreaming/ffmpeg In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/ffmpeg Added Files: stage.zip ffmpeg-segmenter.diff cygwin.zip README make-linux32 make-mingw32 mbuild.sh ffmpeg-version.txt config.guess Log Message: --- NEW FILE: ffmpeg-segmenter.diff --- diff -U 3 -H -w -d -r -N -x .svn -x CVS -x cvs -- ffmpeg-r21659/_configure ffmpeg-for-2.2.5/_configure --- ffmpeg-r21659/_configure 1969-12-31 17:00:00.000000000 -0700 +++ ffmpeg-for-2.2.5/_configure 2009-12-30 17:14:02.000000000 -0700 @@ -0,0 +1,6 @@ +#!/bin/sh +sed s/posix_memalign/posix_memalign_disabled/ configure.orig > configure.orig.fixed +chmod u+x configure.orig.fixed +export MACOSX_DEPLOYMENT_TARGET="10.5" +export CFLAGS="-mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk -I/usr/local/include" +./configure.orig.fixed --enable-pthreads --disable-shared --enable-static --enable-gpl --enable-libx264 --enable-libmp3lame --enable-libfaad --disable-decoder=aac \ No newline at end of file diff -U 3 -H -w -d -r -N -x .svn -x CVS -x cvs -- ffmpeg-r21659/_configure.mingw ffmpeg-for-2.2.5/_configure.mingw --- ffmpeg-r21659/_configure.mingw 1969-12-31 17:00:00.000000000 -0700 +++ ffmpeg-for-2.2.5/_configure.mingw 2009-12-30 17:13:55.000000000 -0700 @@ -0,0 +1,2 @@ +#!/bin/sh +./configure --enable-memalign-hack --prefix=/mingw --target-os=mingw32 --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac \ No newline at end of file diff -U 3 -H -w -d -r -N -x .svn -x CVS -x cvs -- ffmpeg-r21659/_make ffmpeg-for-2.2.5/_make [...10121 lines suppressed...] - for (i = 0; i < 1024*3; i++) - y_table16[i] = av_bswap16(y_table16[i]); - fill_table(c->table_rV, 2, crv, y_table16 + yoffs); - fill_table(c->table_gU, 2, cgu, y_table16 + yoffs + 1024); - fill_table(c->table_bU, 2, cbu, y_table16 + yoffs + 2048); - fill_gv_table(c->table_gV, 2, cgv); - break; case 15: case 16: rbase = isRgb ? bpp - 5 : 0; @@ -796,9 +709,6 @@ y_table16[i+2048] = (yval >> 3) << bbase; yb += cy; } - if(isNotNe) - for (i = 0; i < 1024*3; i++) - y_table16[i] = av_bswap16(y_table16[i]); fill_table(c->table_rV, 2, crv, y_table16 + yoffs); fill_table(c->table_gU, 2, cgu, y_table16 + yoffs + 1024); fill_table(c->table_bU, 2, cbu, y_table16 + yoffs + 2048); --- NEW FILE: cygwin.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: config.guess --- #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 # Free Software Foundation, Inc. timestamp='2008-01-23' # This file 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 2 of the License, or # (at your option) any later version. # # This program 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 [...1487 lines suppressed...] /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: --- NEW FILE: stage.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: mbuild.sh --- #!/bin/bash # Builds the following components: # libfaac # libfaad2 # xvidcore # libx264 # then builds FFmpeg and MPlayer # everything is built in "build" and installed to "stage" so it doesn't clutter up your system # necessary environment variables #export ROOT_DIR=${ROOT_DIR:-`cd ..;pwd`} export ROOT_DIR=${ROOT_DIR:-`pwd`} export CODEC_DIR=${CODEC_DIR:-${ROOT_DIR}/codecs} export FAAC_DIR=${FAAC_DIR:-${CODEC_DIR}/faac} export FAAD2_DIR=${FAAD2_DIR:-${CODEC_DIR}/faad2} export XVID_DIR=${XVID_DIR:-${CODEC_DIR}/xvidcore} export X264_DIR=${X264_DIR:-${CODEC_DIR}/x264} #export FFMPEG_DIR=${FFMPEG_DIR:-${ROOT_DIR}/mplayerhq/ffmpeg} export FFMPEG_DIR=${FFMPEG_DIR:-${CODEC_DIR}/ffmpeg} # place to store incoming tarballs export TARBALL_DIR=${TARBALL_DIR:-`pwd`/packages} # where we'll stage everything export STAGE_DIR=${STAGE:-`pwd`/stage} export STAGE_BIN=${STAGE_BIN:-${STAGE_DIR}/bin} export STAGE_INC=${STAGE_INC:-${STAGE_DIR}/include} export STAGE_LIB=${STAGE_LIB:-${STAGE_DIR}/lib} # temporary build path export BUILD_DIR=${BUILD_DIR:-`pwd`/build} # toolchain path export BIN_DIR=${BIN_DIR:-/opt/mingw/bin} # native tools for host export CC_FOR_BUILD=gcc export NATIVE_HOST=${NATIVE_HOST:-`./config.guess`} require_dir () { if test ! -d "$1"; then echo "Directory missing: \"$1\"" exit 0 fi } # test source paths to make sure everything exists require_dir "${ROOT_DIR}" require_dir "${CODEC_DIR}" require_dir "${FAAC_DIR}" require_dir "${FAAD2_DIR}" require_dir "${XVID_DIR}" require_dir "${X264_DIR}" require_dir "${FFMPEG_DIR}" # cross-compile toolchain commands TARGET=i686-pc-mingw32 CC=${TARGET}-gcc CXX=${TARGET}-g++ LD=${TARGET}-ld NM=${TARGET}-nm AS=${TARGET}-as RANLIB=${TARGET}-ranlib AR=${TARGET}-ar DLLTOOL=${TARGET}-dlltool STRIP=${TARGET}-strip WINDRES=${TARGET}-windres test -d "${STAGE_DIR}" || mkdir "${STAGE_DIR}" test -d "${BUILD_DIR}" || mkdir "${BUILD_DIR}" test -d "${TARBALL_DIR}" || mkdir "${TARBALL_DIR}" # make sure our toolchain is in the PATH echo $PATH | grep -q "${BIN_DIR}" || export PATH=$PATH:"${BIN_DIR}" # libfaac if test ! -f "${FAAC_DIR}/configure" ; then pushd "${FAAC_DIR}" sh bootstrap popd fi mkdir -p "${BUILD_DIR}/faac" pushd "${BUILD_DIR}/faac" test -f Makefile || \ "${FAAC_DIR}"/configure --prefix="${STAGE_DIR}" \ CC=${CC} CXX=${CXX} CFLAGS="-fno-common" \ --enable-static --disable-shared --without-mp4v2 \ --build=${NATIVE_HOST} --host=${TARGET} \ || exit $? test -f libfaac/.libs/libfaac.a || \ make ${MAKE_JOBS} -C libfaac || exit $? test -f "${STAGE_INC}"/faac.h || \ make -C include install || exit $? test libfaac/.libs/libfaac.a -nt "${STAGE_LIB}"/libfaac.a && \ make -C libfaac install popd # libfaad if test ! -f "${FAAD2_DIR}/configure" ; then pushd "${FAAD2_DIR}" sh bootstrap popd fi mkdir -p "${BUILD_DIR}/faad2" pushd "${BUILD_DIR}/faad2" test -f Makefile || \ "${FAAD2_DIR}"/configure --prefix="${STAGE_DIR}" \ CC=${CC} CXX=${CXX} CFLAGS="-fno-common" \ --without-xmms --without-mpeg4ip --without-drm \ --build=${NATIVE_HOST} --host=${TARGET} \ || exit $? make ${MAKE_JOBS} -C libfaad test libfaad/.libs/libfaad.a -nt "${STAGE_LIB}"/libfaad.a && \ make -C libfaad install popd # xvidcore mkdir -p "${BUILD_DIR}/xvidcore" pushd "${BUILD_DIR}/xvidcore" if test ! -f configure ; then rsync -av --cvs-exclude "${XVID_DIR}"/build/generic/* . || exit $? cp sources.inc{,.bak} CLEAN_DIR=`echo ${XVID_DIR} | sed -e 's/ /\\\ /g'` cat sources.inc.bak | sed -e "s|SRC_DIR =.*|SRC_DIR = ${CLEAN_DIR}/src|g" > sources.inc sh bootstrap.sh || exit $? # stupid libtoolize deletes config.{guess,sub} automake -c --add-missing >/dev/null 2>&1 # now fix the pthread detection so it actually uses pthreads # cp configure{,.bak} # cat configure.bak | sed -e "s/-lpthread/-lpthreadGC2 -lws2_32/g" > configure fi if test ! -f platform.inc ; then ./configure --prefix="${STAGE_DIR}" \ CFLAGS="-fno-common" \ --build=${NATIVE_HOST} --host=${TARGET} \ || exit $? fi test -f \=build/xvidcore.a || \ make ${MAKE_JOBS} if test \=build/xvidcore.a -nt "${STAGE_LIB}"/libxvidcore.a; then make install || exit $? rm -f "${STAGE_LIB}"/xvidcore.dll test -f "${STAGE_LIB}"/xvidcore.a && mv "${STAGE_LIB}"/{,lib}xvidcore.a fi popd # libx264 mkdir -p "${BUILD_DIR}"/x264 pushd "${BUILD_DIR}"/x264 if test ! -f configure ; then rsync -av --cvs-exclude "${X264_DIR}"/* . cp configure{,.bak} cat configure.bak | sed -e 's|DEVNULL="NUL"|DEVNULL="/dev/null"|g' > configure fi test -f config.mak || \ CC=${CC} AR=${AR} RANLIB=${RANLIB} STRIP=${STRIP} ./configure --prefix="${STAGE_DIR}" \ --extra-cflags="-fasm -fno-common -D_FILE_OFFSET_BITS=64" \ --disable-avis-input --disable-mp4-output --enable-pthread \ --host=${TARGET} \ || exit $? test -f libx264.a || \ make ${MAKE_JOBS} libx264.a || exit $? # install libx64.a and x264.h manually test libx264.a -nt "${STAGE_LIB}"/libx264.a && install libx264.a "${STAGE_LIB}" test x264.h -nt "${STAGE_LIB}"/x264.h && install x264.h "${STAGE_INC}" popd # ffmpeg mkdir -p "${BUILD_DIR}"/ffmpeg pushd "${BUILD_DIR}"/ffmpeg test -f config.mak || \ ${FFMPEG_DIR}/configure --target-os=mingw32 \ --prefix="${STAGE_DIR}" \ --disable-ffplay --disable-ffserver \ --enable-gpl --enable-nonfree \ --disable-encoder=aac \ --disable-demuxer=ea --disable-devices \ --enable-libxvid --enable-libx264 \ --enable-libfaac \ --enable-static --disable-shared \ --enable-pthreads \ --disable-debug \ --enable-memalign-hack --arch=x86 \ --extra-cflags="-fno-common -march=i686 -mtune=i686 -DWIN32 -I${STAGE_INC}" \ --extra-ldflags="-L${STAGE_LIB}" \ --enable-cross-compile --cross-prefix=${TARGET}- \ || exit $? make ${MAKE_JOBS} || exit $? # installing here ensures the libavXXX.a libraries are available for building MPlayer too make install popd exit 0 --- NEW FILE: ffmpeg-version.txt --- sagetv@blackbird:/opt/sagetv/server$ ./jetty/webapps/SageStreaming/webapp/WEB-INF/bin/ffmpeg -version FFmpeg version UNKNOWN, Copyright (c) 2000-2010 Fabrice Bellard, et al. built on Jul 6 2010 22:33:52 with gcc 4.4.3 configuration: --prefix=/home/jreichen/SageTV/SageStreaming/src-airvideo/installpath --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac --extra-ldflags=-L/home/jreichen/SageTV/SageStreaming/src-airvideo/installpath/lib libavutil 50. 8. 0 / 50. 8. 0 libavcodec 52.52. 0 / 52.52. 0 libavformat 52.50. 0 / 52.50. 0 libavdevice 52. 2. 0 / 52. 2. 0 libswscale 0.10. 0 / 0.10. 0 FFmpeg UNKNOWN libavutil 50. 8. 0 / 50. 8. 0 libavcodec 52.52. 0 / 52.52. 0 libavformat 52.50. 0 / 52.50. 0 libavdevice 52. 2. 0 / 52. 2. 0 libswscale 0.10. 0 / 0.10. 0 sagetv@blackbird:/opt/sagetv/server$ ./ffmpeg FFmpeg version SVN-r67, Copyright (c) 2000-2009 Fabrice Bellard, et al. configuration: --disable-ffserver --disable-ffplay --enable-gpl --enable-pthreads --enable-libfaad --enable-libfaac --enable-libx264 --enable-libxvid --disable-devices --disable-bzlib --prefix=/sage/bin/mplayerhq-sage/libs/ --extra-cflags=-I/sage/bin/mplayerhq-sage/libs/include --extra-ldflags=-L/sage/bin/mplayerhq-sage/libs/lib --disable-demuxer=msnwc_tcp libavutil 50. 2. 0 / 50. 2. 0 libavcodec 52.22. 3 / 52.22. 3 libavformat 52.32. 0 / 52.32. 0 libavdevice 52. 1. 0 / 52. 1. 0 libswscale 0. 7. 1 / 0. 7. 1 built on Jul 14 2010 16:22:41, gcc: 4.4.3 At least one output file must be specified sagetv@blackbird:/opt/sagetv/server$ Write failed: Broken pipe jreichen@intrepid:/media$ ssh sagetv@192.168.25.102 sagetv@192.168.25.102's password: Linux blackbird 2.6.32-22-generic #35-Ubuntu SMP Tue Jun 1 14:17:36 UTC 2010 i686 GNU/Linux Ubuntu 10.04 LTS Welcome to Ubuntu! * Documentation: https://help.ubuntu.com/ System information as of Sun Aug 1 19:39:21 MDT 2010 System load: 0.02 Memory usage: 50% Processes: 179 Usage of /: 74.7% of 24.04GB Swap usage: 4% Users logged in: 1 => /media/sda1 is using 93.5% of 465.63GB => /media/sdb1 is using 86.8% of 233.64GB Graph this data and manage this system at https://landscape.canonical.com/ You have mail. Last login: Sat Jul 31 21:31:05 2010 from intrepid.local cdsagetv@blackbird:~$ cd /opt/sagetv/server sagetv@blackbird:/opt/sagetv/server$ ./jetty/webapps/SageStreaming/webapp/WEB-INF/bin/ffmpeg -version FFmpeg version 0.6, Copyright (c) 2000-2010 the FFmpeg developers built on Aug 1 2010 19:31:16 with gcc 4.4.3 configuration: --prefix=/sagebuild --arch=i686 --cpu=i686 --disable-mmx --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac --extra-ldflags=-L/sagebuild/lib libavutil 50.15. 1 / 50.15. 1 libavcodec 52.72. 2 / 52.72. 2 libavformat 52.64. 2 / 52.64. 2 libavdevice 52. 2. 0 / 52. 2. 0 libswscale 0.11. 0 / 0.11. 0 FFmpeg 0.6 libavutil 50.15. 1 / 50.15. 1 libavcodec 52.72. 2 / 52.72. 2 libavformat 52.64. 2 / 52.64. 2 libavdevice 52. 2. 0 / 52. 2. 0 libswscale 0.11. 0 / 0.11. 0 sagetv@blackbird:/opt/sagetv/server$ --- NEW FILE: make-linux32 --- #!/bin/bash # # save current variables # cur_dir=`pwd` cur_path=$PATH cur_include_path=$C_INCLUDE_PATH cur_library_path=$LIBRARY_PATH cur_cflags=$CFLAGS cur_ldflags=$LDFLAGS # everything is built in "build" and installed to "stage" so it doesn't clutter up your system # necessary environment variables #export ROOT_DIR=${ROOT_DIR:-`cd ..;pwd`} export ROOT_DIR=${ROOT_DIR:-`pwd`} export CODEC_DIR=${CODEC_DIR:-${ROOT_DIR}/codecs} export FAAC_DIR=${FAAC_DIR:-${CODEC_DIR}/faac} export FAAD2_DIR=${FAAD2_DIR:-${CODEC_DIR}/faad2} export XVID_DIR=${XVID_DIR:-${CODEC_DIR}/xvidcore} export X264_DIR=${X264_DIR:-${CODEC_DIR}/x264} #export FFMPEG_DIR=${FFMPEG_DIR:-${ROOT_DIR}/mplayerhq/ffmpeg} export FFMPEG_DIR=${FFMPEG_DIR:-${CODEC_DIR}/ffmpeg} # where we'll stage everything #export STAGE_DIR=${STAGE:-`pwd`/stage} #export STAGE_BIN=${STAGE_BIN:-${STAGE_DIR}/bin} #export STAGE_INC=${STAGE_INC:-${STAGE_DIR}/include} #export STAGE_LIB=${STAGE_LIB:-${STAGE_DIR}/lib} # temporary build path export BUILD_DIR=${BUILD_DIR:-`pwd`/build} # create install directory install_dir=/sagebuild if [ ! -d $install_dir ]; then mkdir $install_dir fi # # set variables # export CFLAGS="-I$install_dir/include -L$install_dir/lib" #export LDFLAGS="-L$install_dir/lib" #export C_INCLUDE_PATH=$install_dir/include:$C_INCLUDE_PATH #export LIBRARY_PATH=$install_dir/lib:$LIBRARY_PATH export PATH=$install_dir/bin:$PATH # # yasm dependency # #cd $cur_dir/yasm-1.0.1 #make clean #./configure --prefix=$install_dir #make #make install #make clean #make z #if [ $? -eq 0 ] ; then # do stuff when make succeeds #else # do stuff when make fails # echo Yasm failed # exit $?; #fi # # libx264 dependency # #cd $cur_dir/x264-snapshot-20100801-2245 #make clean #./configure --prefix=$install_dir #make #make install # # lame dependency # #cd $cur_dir/lame-3.98.4 #make clean #./configure --prefix=$install_dir #make #make install # # faad dependency # #cd $cur_dir/faad2-2.7 #make clean #./configure --prefix=$install_dir #make #make install # # faac dependency # won't build mkdir -p "${BUILD_DIR}/faac" pushd "${BUILD_DIR}/faac" #pushd "$cur_dir/codecs/faac" make clean test -f Makefile || \ "${FAAC_DIR}"/configure --prefix=$install_dir --enable-static --disable-shared --without-mp4v2 \ || exit $? make make install popd # # xvid dependency # #cd $cur_dir/xvidcore-1.2.2/build/generic #make clean #./configure --prefix=$install_dir #make #make install # # ffmpeg # mkdir -p "${BUILD_DIR}"/ffmpeg pushd "${BUILD_DIR}"/ffmpeg # cd $cur_dir/ffmpeg-pure-airvideo # cd $cur_dir/ffmpeg-sage-7.0.13 # cd $cur_dir/ffmpeg-sage-7.0.13-and-segmenter-2.2.5 chmod +x "${FFMPEG_DIR}"/configure chmod +x "${FFMPEG_DIR}"/version.sh # make clean # "${FFMPEG_DIR}"/configure --prefix=$install_dir --arch=i686 --cpu=i686 --disable-mmx --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac --extra-ldflags=-L$install_dir/lib # "${FFMPEG_DIR}"/configure --prefix=$install_dir --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --enable-libfaac --extra-ldflags=-L$install_dir/lib # "${FFMPEG_DIR}"/configure --prefix=$install_dir --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --extra-ldflags=-L$install_dir/lib #sage # "${FFMPEG_DIR}"/configure --disable-ffserver --disable-ffplay --enable-gpl --enable-pthreads --enable-nonfree --enable-libfaac --enable-libx264 --enable-libxvid --disable-devices --disable-bzlib --prefix=/sage/bin/mplayerhq-sage/libs/ --extra-cflags=-I/sage/bin/mplayerhq-sage/libs/include --extra-ldflags=-L/sage/bin/mplayerhq-sage/libs/lib --disable-demuxer=msnwc_tcp #test -f config.mak || \ "${FFMPEG_DIR}/configure" --prefix="${install_dir}" --disable-ffserver --disable-ffplay --enable-gpl --enable-pthreads --enable-nonfree --enable-libfaac --enable-libx264 --enable-libxvid --disable-devices --disable-bzlib --extra-cflags="-I${install_dir}/include" --extra-ldflags="-L${install_dir}/lib" --disable-demuxer=msnwc_tcp # || exit $? # --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib make || exit $? make install # cp $install_dir/bin/ffmpeg $cur_dir/../bin/linux32 popd # # reset variables # LDFLAGS=$cur_ldflags CFLAGS=$cur_cflags LIBRARY_PATH=$cur_library_path C_INCLUDE_PATH=$cur_include_path PATH=$cur_path cd $cur_dir --- NEW FILE: README --- Merging SageTV's patched ffmpeg with AirVideo's patched ffmpeg (with segmenter) =============================================================================== SageTV 7.0.13's ffmpeg is based on svn revision 24249. There are two files from Sage. ffmpeg-sage-7.0.13.zip is their full ffmpeg patched source. libavformat-sage- 7.0.13.zip is a patch for usleep. Apply the second file to the first and save in the ffmpeg-sage-7.0.13 directory. (These merges should already be applied but are mentioned here to document the steps). AirVideo's ffmpeg as of 7/31/10 was mainly based on svn revision 21569. It includes ffmpeg, an HTTP Live Streaming segmenter, and configure scripts for Windows and Mac. The latest revision can be downloaded from http://www.inmethod.com/air-video/licenses.html. Direct link: http://www.inmethod.com/air-video/download/ffmpeg-for-2.2.5.tar.bz2. ffmpeg-segmenter.diff was created using the Kompare tool by comparing the ffmpeg-r21659 and ffmpeg-for-2.2.5 directories. This is a list of all the changes made by InMethod AirVideo. This set of changes is then applied to the ffmpeg-sage-7.0.13-and-segmenter-2.2.5 directory. I patched the following files from InMethod AirVideo. Also merge any new files (mainly those that begin with an underscore '_'). Don't worry about libswscale changes. libavcodec/resample.c libavformat/mpegtsenc.c libavformat/os_support.c/h ffmpeg.c (and maybe configure) ffmpeg source can be retrieved using the following commands in Linux: svn checkout svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg-r21587 -r 21587 svn checkout svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg-r21622 -r 21622 svn checkout svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg-r21659 -r 21659 svn checkout svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg-r24249 -r 24249 Build scripts for each platform are (TODO, this is not accurate or complete): make-linux32 make-macos make-mingw32/mbuild.sh (windows) On Windows, cygwin must be set up before building anything else. A copy of cygwin used to build the binaries is in the cygwin.zip file. http://www.cygwin.com Several dependencies need to be downloaded and built before building ffmpeg (copies of these files are in the 'packages' directory): AirVideo Server http://www.inmethod.com/air-video/licenses.html libx264 ftp://ftp.videolan.org/pub/videolan/x264/snapshots/ faad2, faac http://www.audiocoding.com/downloads.html lame http://sourceforge.net/projects/lame/files/lame/ yasm http://www.tortall.net/projects/yasm/wiki/Download xvidcore ?/xvidcore-1.2.2.tar.gz would not build, use xvidcore-bin.zip instead A few notes to myself: building ffmpeg http://webcache.googleusercontent.com/search?q=cache:Auk-5AZuERkJ:www.cihilt.com/book/export/html/49+ffmpeg+%22-L/usr/local/lib%22&cd=28&hl=en&ct=clnk&gl=us If Libraries couldnt be found and it gave error message on make, try to run the ffmpeg configure lines with path to libs ./configure --enable-shared --enable-libmp3lame --enable-xvid --enable-libvorbis --enable-libogg --enable-gpl --enable-libfaad --enable-libfaac --enable-x264 --enable-amr_nb --enable-amr_nb-fixed --enable-pp --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib make make install --- NEW FILE: make-mingw32 --- #!/bin/bash # # save current variables # cur_dir=`pwd` cur_path=$PATH cur_include_path=$C_INCLUDE_PATH cur_library_path=$LIBRARY_PATH cur_cflags=$CFLAGS cur_ldflags=$LDFLAGS # create install directory install_dir=/sagebuild-mingw32 #install_dir=/c/sagebuild if [ ! -d $install_dir ]; then mkdir $install_dir fi # # set variables # export CFLAGS="-I$install_dir/include -L$install_dir/lib" #export LDFLAGS="-L$install_dir/lib" #export C_INCLUDE_PATH=$install_dir/include:$C_INCLUDE_PATH #export LIBRARY_PATH=$install_dir/lib:$LIBRARY_PATH export PATH=$install_dir/bin:$PATH # # yasm dependency # #cd $cur_dir/yasm-1.0.1 #make clean #./configure --prefix=$install_dir #make #make install # # libx264 dependency # #cd $cur_dir/x264-snapshot-20100801-2245 #make clean #./configure --prefix=$install_dir #make #make install # # lame dependency # #cd $cur_dir/lame-3.98.4 #make clean #./configure --prefix=$install_dir #make #make install # # faad dependency # #cd $cur_dir/faad2-2.7 #make clean #./configure --prefix=$install_dir #make #make install # # faac dependency # won't build #cd $cur_dir/faac-1.28 #make clean #./configure --prefix=$install_dir #make #make install # # xvid dependency # #cd $cur_dir/xvidcore-1.2.2/build/generic #make clean #./configure --prefix=$install_dir #make #make install # # ffmpeg # #echo $CFLAGS date #cd $cur_dir/ffmpeg-pure-airvideo #cd $cur_dir/ffmpeg-0.6 #cd $cur_dir/ffmpeg-sage-7.0.13 cd $cur_dir/ffmpeg-sage-7.0.13-and-segmenter-2.2.5 chmod +x configure chmod +x version.sh make clean #./configure --prefix=$install_dir --arch=i686 --cpu=i686 --disable-mmx --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac --extra-ldflags=-L$install_dir/lib #./configure --prefix=$install_dir --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --enable-libfaac --extra-ldflags=-L$install_dir/lib #./configure --prefix=$install_dir --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --extra-ldflags=-L$install_dir/lib ##sage #./configure --disable-ffserver --disable-ffplay --enable-gpl --enable-pthreads --enable-nonfree --enable-libfaac --enable-libx264 --enable-libxvid --disable-devices --disable-bzlib --prefix=/sage/bin/mplayerhq-sage/libs/ --extra-cflags=-I/sage/bin/mplayerhq-sage/libs/include --extra-ldflags=-L/sage/bin/mplayerhq-sage/libs/lib --disable-demuxer=msnwc_tcp #./configure --prefix=$install_dir --disable-ffserver --disable-ffplay --enable-gpl --enable-pthreads --enable-nonfree --enable-libfaac --enable-libx264 --enable-libxvid --disable-devices --disable-bzlib --extra-cflags=-I/$install_dir/include --extra-ldflags=-L/$install_dir/lib --disable-demuxer=msnwc_tcp #./configure --enable-memalign-hack --prefix=/mingw --target-os=mingw32 --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac #./configure --enable-memalign-hack --prefix=$install_dir --target-os=mingw32 --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --enable-libfaad --disable-decoder=aac #./configure --enable-memalign-hack --prefix=$install_dir --arch=i686 --cpu=i686 --enable-gpl --enable-libx264 --enable-static --disable-shared --enable-pthreads --enable-libmp3lame --disable-decoder=aac ##sage-linux #./configure --target-os=mingw32 --prefix=/cygdrive/c/dev/src/buildwin/stage --disable-ffplay --disable-ffserver --enable-gpl --enable-nonfree --disable-encoder=aac --disable-demuxer=ea --disable-devices --enable-libxvid --enable-libx264 --enable-libfaac --enable-static --disable-shared --enable-pthreads --disable-debug --enable-memalign-hack --arch=x86 --extra-cflags='-fno-common -march=i686-mtune=i686 -DWIN32 -I/cygdrive/c/dev/src/buildwin/stage/include' --extra-ldflags=-L/cygdrive/c/dev/src/buildwin/stage/lib --enable-cross-compile --cross-prefix=i686-pc-mingw32- #./configure --target-os=mingw32 --prefix=/c/sagebuild --disable-ffplay --disable-ffserver --enable-gpl --enable-nonfree --disable-encoder=aac --disable-demuxer=ea --disable-devices --enable-libxvid --enable-libx264 --enable-libfaac --enable-static --disable-shared --enable-pthreads --disable-debug --enable-memalign-hack --arch=x86 --extra-cflags='-fno-common -march=i686-mtune=i686 -DWIN32 -I/c/sagebuild/include' --extra-ldflags=-L/c/sagebuild/lib --enable-cross-compile --cross-prefix=i686-pc-mingw32- ./configure --target-os=mingw32 --prefix=$install_dir --disable-ffplay --disable-ffserver --enable-gpl --enable-nonfree --disable-encoder=aac --disable-demuxer=ea --disable-devices --enable-libxvid --enable-libx264 --enable-libfaac --enable-static --disable-shared --enable-pthreads --disable-debug --enable-memalign-hack --arch=x86 --extra-cflags='-fno-common -march=i686-mtune=i686 -DWIN32 -I$install_dir/include' --extra-ldflags=-L$install_dir/lib --enable-cross-compile --cross-prefix=i586-mingw32msvc- #--extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib make make install date #cp $install_dir/bin/ffmpeg $cur_dir/../bin/win32 # # reset variables # LDFLAGS=$cur_ldflags CFLAGS=$cur_cflags LIBRARY_PATH=$cur_library_path C_INCLUDE_PATH=$cur_include_path PATH=$cur_path cd $cur_dir |
From: jreichen <jre...@us...> - 2010-11-03 03:42:38
|
Update of /cvsroot/sageplugins/MediaStreaming/resources/contexts In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/resources/contexts Added Files: mediastreaming.xml Log Message: --- NEW FILE: mediastreaming.xml --- <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd"> <Configure class="org.mortbay.jetty.webapp.WebAppContext"> <Set name="contextPath">/stream</Set> <!--Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/webapps/MediaStreaming</Set--> <Set name="war"> <SystemProperty name="jetty.home" default="."/>/webapps/MediaStreaming.war </Set> <Set name="tempDirectory"> <SystemProperty name="jetty.home" default="."/>/webapps/MediaStreaming </Set> <Set name="defaultsDescriptor"> <SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml </Set> <Set name="extractWAR">true</Set> <Set name="copyWebDir">true</Set> <Call name="setAttribute"> <Arg>pluginid</Arg> <Arg>mediastreaming</Arg> </Call> <Call name="setAttribute"> <Arg>webpage</Arg> <Arg>/index.jsp</Arg> </Call> <Set name="mimeTypes"> <New id="mimeTypes" class="sagex.jetty.content.MimeTypes"/> </Set> <!-- http://docs.codehaus.org/display/JETTY/How+to+Configure+Security+with+Embedded+Jetty --> <!-- include security constraints here because the only other place they can be specified is in the web.xml file inside the war file --> <!-- can the constraints be included in another file we won't overwrite? --> <Get name="securityHandler"> <Set name="userRealm"> <New class="org.mortbay.jetty.security.HashUserRealm"> <Set name="name">SageTV Web Interface</Set> <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set> </New> </Set> <Set name="checkWelcomeFiles">true</Set> <Set name="constraintMappings"> <!-- TODO create unprotected public area --> <Array type="org.mortbay.jetty.security.ConstraintMapping"> <Item> <New class="org.mortbay.jetty.security.ConstraintMapping"> <Set name="constraint"> <New class="org.mortbay.jetty.security.Constraint"> <Set name="name">BASIC</Set> <Set name="roles"> <Array type="java.lang.String"> <Item>user</Item> <Item>admin</Item> <Item>moderator</Item> </Array> </Set> <Set name="authenticate">true</Set> </New> </Set> <Set name="pathSpec">/*</Set> </New> </Item> <!--Item> <New class="org.mortbay.jetty.security.ConstraintMapping"> <Set name="constraint"> <New class="org.mortbay.jetty.security.Constraint"> <Set name="name">BASIC</Set> <Set name="roles"> <Array type="java.lang.String"> <Item>admin</Item> </Array> </Set> <Set name="authenticate">true</Set> </New> </Set> <Set name="pathSpec">/m/Command</Set> </New> </Item--> </Array> </Set> </Get> </Configure> |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/io In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/src/sagex/streaming/io Added Files: ChannelGobbler.java SegmentInputStream.java StreamGobbler.java SubsegmentInputStream.java Log Message: --- NEW FILE: StreamGobbler.java --- package sagex.streaming.io; import java.io.BufferedInputStream; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.util.concurrent.Callable; import org.mortbay.jetty.EofException; import org.mortbay.log.Log; // TODO redirect to Log? // http://www.javaspecialists.eu/archive/Issue056.html public class StreamGobbler /*extends Thread//*/implements Callable<Object>, Closeable, Flushable { private String name; private InputStream is; private OutputStream os; private boolean autoClose = true; private volatile Thread currentThread = null; public StreamGobbler(String name, InputStream is) { this(name, is, null); } public StreamGobbler(String name, InputStream is, OutputStream redirect) { this(name, is, redirect, true); } public StreamGobbler(String name, InputStream is, OutputStream redirect, boolean autoClose) { this.name = name; this.is = new BufferedInputStream(is); this.os = redirect; this.autoClose = autoClose; } public synchronized void interrupt() { if (currentThread != null) { currentThread.interrupt(); } } public synchronized void flush() { if (os != null) { try { os.flush(); } catch (IOException e) { // ignore } } } public synchronized void close() { close(is); close(os); } private synchronized void close(Closeable c) { if (c != null) { try { c.close(); } catch (Exception e) {} // quietly close c = null; } } public Object call() throws Exception { currentThread = Thread.currentThread(); String oldName = currentThread.getName(); long totalBytes = 0; try { if (name != null) { currentThread.setName(name + " (" + oldName + ")"); } byte[] buf = new byte[4096]; int bytesRead = 0; while ((bytesRead = is.read(buf, 0, buf.length)) > 0) { totalBytes += bytesRead; if (os != null) { // Log.debug("Bytes read in StreamGobbler [" + Thread.currentThread().getName() + "] " + totalBytes); os.write(buf, 0, bytesRead); } else { String s = new String(buf, 0, bytesRead); System.out.println(s); } } System.out.println("StreamGobbler bytesRead = " + bytesRead); flush(); } catch (EofException e) { // an error occurred writing to the output stream. if auto close // is not set, then the stream is the output stream to the client close(os); if (autoClose) { // unexpected EOF, log the exception // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); } else { // EOF can be expected since the client may disconnect and that's // not an error that should be reported System.out.println("Client disconnected"); } throw e; } catch (InterruptedIOException e) { // close input and output streams when interrupted close(); currentThread.interrupt(); System.out.println("Interrupted via InterruptedIOException"); } catch (IOException e) { if (!currentThread.isInterrupted()) { System.out.println("StreamGobbler encountered exception " + e.getMessage() + " and is exiting."); // Log.info(e.getMessage()); // Log.ignore(e); throw e; } else { // close input and output streams when interrupted close(); currentThread.interrupt(); System.out.println("Interrupted"); } } finally { flush(); if (autoClose) { close(os); } System.out.println("Total bytes read in StreamGobbler [" + currentThread.getName() + "] " + totalBytes); System.out.println("StreamGobbler [" + currentThread.getName() + "] exiting"); currentThread.setName(oldName); currentThread = null; } return null; } } --- NEW FILE: SubsegmentInputStream.java --- package sagex.streaming.io; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import org.mortbay.log.Log; /** * Returns an HTTP Live Streaming segment as each sub segment is produced by the segmenter. * The entire segment length is not available therefore the HTTP Content-Length response * header cannot be included in the response. */ public class SubsegmentInputStream extends InputStream { private volatile DataInputStream dis = null; private volatile long segmentSize = 0; // can grow until isSegmentFinished == true private volatile boolean isSegmentFinished = false; private volatile long bytesAvailable = 0; // can grow until isSegmentFinished == true public SubsegmentInputStream(InputStream is) { this.dis = new DataInputStream(is); } @Override public int available() throws IOException { // Get the next subsegment // if ((bytesAvailable == 0) && (!isSegmentFinished)) // { // readSegmentFinished(); // readSubsegmentSize(); // } // throw new UnsupportedOperationException("available"); if (bytesAvailable > Integer.MAX_VALUE) { throw new IllegalStateException("integer overflow"); } return (int) bytesAvailable; } @Override public void close() throws IOException { dis.close(); } @Override public synchronized void mark(int arg0) { throw new UnsupportedOperationException("mark"); } @Override public boolean markSupported() { return false; } @Override public int read() throws IOException { if (bytesAvailable > 0) { int value = dis.read(); if (value == -1) { throw new IllegalStateException("Segment still has bytes available but no byte was returned."); } bytesAvailable -= value; return value; } // // At this point we know there are zero bytes available // // return -1 to indicate the segment is finished, then reset so the // next read will start returning the next segment if (isSegmentFinished) { System.err.println("Segment size " + segmentSize); isSegmentFinished = false; segmentSize = 0; return -1; } else { // Get the next subsegment readSegmentFinished(); readSubsegmentSize(); // TODO try to refactor so infinite recursion isn't possible return this.read(); // bytesAvailable -= 1; // return dis.read(); } } @Override public int read(byte[] b, int off, int len) throws IOException { // The current subsegment has enough bytes to fulfill the request if (bytesAvailable >= len) { int bytesRead = dis.read(b, off, len); if (bytesRead == -1) { throw new IllegalStateException("Subsegment still has more than the requested number of bytes available but no bytes were returned."); } bytesAvailable -= bytesRead; return bytesRead; } // The current subsegment has somewhere between 1 and // the number of bytes requested. Return the remaining // bytes of this subsegment. if (bytesAvailable > 0) { int bytesRead = dis.read(b, off, (int) Math.min(Integer.MAX_VALUE, bytesAvailable)); if (bytesRead == -1) { throw new IllegalStateException("Subsegment still has bytes available but no bytes were returned."); } bytesAvailable -= bytesRead; return bytesRead; } // // At this point we know there are zero bytes available // // return -1 to indicate the segment is finished, then reset so the // next read will start returning the next segment if (isSegmentFinished) { System.out.println("Segment size " + segmentSize); isSegmentFinished = false; segmentSize = 0; return -1; } else { // Get the next subsegment readSegmentFinished(); readSubsegmentSize(); // TODO try to refactor so infinite recursion isn't possible return this.read(b, off, len); // bytesAvailable -= 1; // return dis.read(); } } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public synchronized void reset() throws IOException { throw new UnsupportedOperationException("reset"); } @Override public long skip(long arg0) throws IOException { throw new UnsupportedOperationException("skip"); } public long getSegmentLength() { return segmentSize; } // Read an unsigned 32-bit integer. If it's '1' then all the // segment is available. If it's '0' then some of the segment // still has yet to be written to the stream on the other end. private void readSegmentFinished() throws IOException { byte[] finished = new byte[4]; try { dis.readFully(finished); isSegmentFinished = (finished[3] == 1); // System.err.println("Finished: " + finished[0] + " " + finished[1] + " " + finished[2] + " " + finished[3]); } catch (Exception e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); } } // Read an unsigned 32-bit integer. This is the size of the segment // that needs to be made available by this stream. private void readSubsegmentSize() throws IOException { byte[] subsegmentSizeBytes = new byte[4]; dis.readFully(subsegmentSizeBytes); try { long subsegmentSize = 0; subsegmentSize = (((long)(subsegmentSizeBytes[0] & 0xff) << 24) | ((long)(subsegmentSizeBytes[1] & 0xff) << 16) | ((long)(subsegmentSizeBytes[2] & 0xff) << 8) | ((long)(subsegmentSizeBytes[3] & 0xff))); segmentSize += subsegmentSize; bytesAvailable += subsegmentSize; // System.err.println("Size: " + subsegmentSize + "/" + segmentSize); } catch (Exception e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); } } } --- NEW FILE: SegmentInputStream.java --- package sagex.streaming.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; /** * Returns an HTTP Live Streaming segment, blocking until the entire segment is ready. * The entire segment length is available after the segment has been buffered therefore * the HTTP Content-Length response header can be included in the response. */ public class SegmentInputStream extends InputStream { private volatile DataInputStream dis = null; private volatile ByteArrayInputStream bais = null; private volatile long segmentSize = 0; // can grow until isSegmentFinished == true private volatile boolean isSegmentFinished = false; // private volatile long bytesAvailable = 0; // can grow until isSegmentFinished == true public SegmentInputStream(InputStream is) { this.dis = new DataInputStream(is); } @Override public int available() throws IOException { return (bais == null) ? 0 : bais.available(); // Get the next subsegment //// if ((bytesAvailable == 0) && (!isSegmentFinished)) //// { //// readSegmentFinished(); //// readSubsegmentSize(); //// } // //// throw new UnsupportedOperationException("available"); // if (bytesAvailable > Integer.MAX_VALUE) // { // throw new IllegalStateException("integer overflow"); // } //// return (int) bytesAvailable; } @Override public void close() throws IOException { dis.close(); bais = null; } @Override public synchronized void mark(int arg0) { if (bais == null) { throw new IllegalStateException("Buffer is null"); } bais.mark(arg0); // throw new UnsupportedOperationException("mark"); } @Override public boolean markSupported() { if (bais == null) { throw new IllegalStateException("Buffer is null"); } return bais.markSupported(); // return false; } @Override public int read() throws IOException { if (bais == null) { throw new IllegalStateException("Buffer is null"); } return bais.read(); // if (bytesAvailable > 0) // { // int value = dis.read(); // if (value == -1) // { // throw new IllegalStateException("Segment still has bytes available but no byte was returned."); // } // bytesAvailable -= value; // return value; // } // // // // // At this point we know there are zero bytes available // // // // // return -1 to indicate the segment is finished, then reset so the // // next read will start returning the next segment // if (isSegmentFinished) // { // System.out.println("Segment size " + segmentSize); // isSegmentFinished = false; // segmentSize = 0; // return -1; // } // else // { // // Get the next subsegment // readSegmentFinished(); // readSubsegmentSize(); // // // TODO try to refactor so infinite recursion isn't possible // return this.read(); //// bytesAvailable -= 1; //// return dis.read(); // } } @Override public int read(byte[] b, int off, int len) throws IOException { if (bais == null) { throw new IllegalStateException("Buffer is null"); } return bais.read(b, off, len); // // The current subsegment has enough bytes to fulfill the request // if (bytesAvailable >= len) // { // int bytesRead = dis.read(b, off, len); // if (bytesRead == -1) // { // throw new IllegalStateException("Subsegment still has more than the requested number of bytes available but no bytes were returned."); // } // bytesAvailable -= bytesRead; // return bytesRead; // } // // // The current subsegment has somewhere between 1 and // // the number of bytes requested. Return the remaining // // bytes of this subsegment. // if (bytesAvailable > 0) // { // int bytesRead = dis.read(b, off, (int) Math.min(Integer.MAX_VALUE, bytesAvailable)); // if (bytesRead == -1) // { // throw new IllegalStateException("Subsegment still has bytes available but no bytes were returned."); // } // bytesAvailable -= bytesRead; // return bytesRead; // } // // // // // At this point we know there are zero bytes available // // // // // return -1 to indicate the segment is finished, then reset so the // // next read will start returning the next segment // if (isSegmentFinished) // { // System.out.println("Segment size " + segmentSize); // isSegmentFinished = false; // segmentSize = 0; // return -1; // } // else // { // // Get the next subsegment // readSegmentFinished(); // readSubsegmentSize(); // // // TODO try to refactor so infinite recursion isn't possible // return this.read(b, off, len); //// bytesAvailable -= 1; //// return dis.read(); // } } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public synchronized void reset() throws IOException { if (bais == null) { throw new IllegalStateException("Buffer is null"); } bais.reset(); // throw new UnsupportedOperationException("reset"); } @Override public long skip(long arg0) throws IOException { if (bais == null) { throw new IllegalStateException("Buffer is null"); } return bais.skip(arg0); // throw new UnsupportedOperationException("skip"); } public void fillNextSegment() { isSegmentFinished = false; segmentSize = 0; try { // baos.reset(); ByteArrayOutputStream baos = new ByteArrayOutputStream(500000); System.out.println("Initial ByteArrayOutputStream size: " + baos.size()); do { // Get the next subsegment readSegmentFinished(); int subsegmentSize = (int) readSubsegmentSize(); // TODO throw exception if (subsegmentSize > 0) { segmentSize += subsegmentSize; byte[] b = new byte[subsegmentSize]; dis.readFully(b); baos.write(b); } } while (!isSegmentFinished); System.out.println("Final ByteArrayOutputStream size: " + baos.size()); bais = new ByteArrayInputStream(baos.toByteArray()); baos = null; } catch (IOException e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); } } public long getSegmentLength() { // return bais.size(); return segmentSize; } // Read an unsigned 32-bit integer. If it's '1' then all the // segment is available. If it's '0' then some of the segment // still has yet to be written to the stream on the other end. private void readSegmentFinished() throws IOException { byte[] finished = new byte[4]; try { dis.readFully(finished); isSegmentFinished = (finished[3] == 1); // System.err.println("Finished: " + finished[0] + " " + finished[1] + " " + finished[2] + " " + finished[3]); } catch (Exception e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); } } // Read an unsigned 32-bit integer. This is the size of the segment // that needs to be made available by this stream. private long readSubsegmentSize() throws IOException { byte[] subsegmentSizeBytes = new byte[4]; dis.readFully(subsegmentSizeBytes); long subsegmentSize = 0; try { subsegmentSize = (((long)(subsegmentSizeBytes[0] & 0xff) << 24) | ((long)(subsegmentSizeBytes[1] & 0xff) << 16) | ((long)(subsegmentSizeBytes[2] & 0xff) << 8) | ((long)(subsegmentSizeBytes[3] & 0xff))); // segmentSize += subsegmentSize; // bytesAvailable += subsegmentSize; // System.err.println("Size: " + subsegmentSize + "/" + segmentSize); } catch (Exception e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); } return subsegmentSize; } } --- NEW FILE: ChannelGobbler.java --- package sagex.streaming.io; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import org.mortbay.log.Log; public class ChannelGobbler extends Thread { private String name; private ReadableByteChannel input; private WritableByteChannel output; private OutputStream outputStream; public ChannelGobbler(String name, ReadableByteChannel input) { this(name, input, System.out); } public ChannelGobbler(String name, ReadableByteChannel input, OutputStream redirect) { this(name, input, Channels.newChannel(redirect)); this.outputStream = redirect; } public ChannelGobbler(String name, ReadableByteChannel input, WritableByteChannel redirect) { this.name = name; this.input = input; this.output = redirect; } public void run() { long totalBytes = 0; try { if (name != null) { Thread.currentThread().setName(name); } ByteBuffer buffer = ByteBuffer.allocateDirect(1024); int bytesRead = 0; while (((bytesRead = input.read(buffer)) > -1) || (buffer.position() > 0)) { if (isInterrupted()) { break; } Log.debug("Bytes read in ChannelGobbler [" + name + "] " + bytesRead); totalBytes += bytesRead; buffer.flip(); Log.debug("Total bytes written " + totalBytes); if (output != null) { output.write(buffer); if (outputStream != null) { // outputStream.flush(); } } else { Charset charset = Charset.forName("UTF-16"); CharsetDecoder charDecoder = charset.newDecoder(); String s = charDecoder.decode(buffer).toString(); Log.debug(s); } // output.flush(); Log.debug("Bytes written in ChannelGobbler [" + name + "] " + bytesRead); buffer.compact(); // if (os != null) // { // totalBytes += bytesRead; // os.write(buf, 0, bytesRead); // os.flush(); // } // else // { // String s = new String(buf, 0, bytesRead); // Log.debug(s); // } } // if (os != null) // { // os.flush(); // } } catch (IOException ioe) { Log.debug("ChannelGobbler [" + name + "] encountered exception " + ioe.getMessage() + " and is exiting."); Log.info(ioe.getMessage()); Log.ignore(ioe); } finally { // if (os != null) // { // try // { // os.flush(); // os.close(); // } // catch (IOException e) // { // } // } Log.debug("Total bytes read in ChannelGobbler [" + name + "] " + totalBytes); Log.debug("ChannelGobbler [" + name + "] exiting"); } } } |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent/META-INF In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/WebContent/META-INF Added Files: MANIFEST.MF Log Message: --- NEW FILE: MANIFEST.MF --- Manifest-Version: 1.0 Class-Path: |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/WebContent Added Files: index.jsp Log Message: --- NEW FILE: index.jsp --- <?xml version="1.0" encoding="UTF-8" ?> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@page import="sagex.api.MediaFileAPI"%> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>SageTV</title> </head> <body> </body> </html> |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/WebContent/WEB-INF Added Files: web.xml Log Message: --- NEW FILE: web.xml --- <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>SageTV Media Streaming Services</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>MediaFileServlet</display-name> <servlet-name>MediaFileServlet</servlet-name> <servlet-class>sagex.streaming.servlet.MediaFileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MediaFileServlet</servlet-name> <url-pattern>/MediaFileServlet/*</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>MediaFileThumbnailServlet</display-name> <servlet-name>MediaFileThumbnailServlet</servlet-name> <servlet-class>sagex.streaming.servlet.MediaFileThumbnailServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MediaFileThumbnailServlet</servlet-name> <url-pattern>/MediaFileThumbnailServlet/*</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>HTTPLiveStreamingPlaylistServlet</display-name> <servlet-name>HTTPLiveStreamingPlaylistServlet</servlet-name> <servlet-class>sagex.streaming.servlet.HTTPLiveStreamingPlaylistServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HTTPLiveStreamingPlaylistServlet</servlet-name> <url-pattern>/HTTPLiveStreamingPlaylist/*</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>HTTPLiveStreamingSegmentServlet</display-name> <servlet-name>HTTPLiveStreamingSegmentServlet</servlet-name> <servlet-class>sagex.streaming.servlet.HTTPLiveStreamingSegmentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HTTPLiveStreamingSegmentServlet</servlet-name> <url-pattern>/HTTPLiveStreamingSegment/*</url-pattern> </servlet-mapping> <listener> <listener-class> sagex.streaming.listener.AppContextListener </listener-class> </listener> </web-app> |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/bin/linux32 In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/bin/linux32 Added Files: ffmpeg Log Message: --- NEW FILE: ffmpeg --- (This appears to be a binary file; contents omitted.) |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/ffmpeg/packages In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/ffmpeg/packages Added Files: ffmpeg-sage-7.0.13.zip x264-snapshot-20100801-2245.tar.bz2 ffmpeg-sage-7.0.13-and-segmenter-2.2.zip lame-3.98.4.tar.gz faad2-2.7.tar.gz ffmpeg-for-2.2.5.tar.bz2 xvidcore-bin.zip faac-1.28.tar.gz xvidcore-1.2.2.tar.gz yasm-1.0.1.tar.gz Log Message: --- NEW FILE: ffmpeg-sage-7.0.13.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: x264-snapshot-20100801-2245.tar.bz2 --- (This appears to be a binary file; contents omitted.) --- NEW FILE: xvidcore-1.2.2.tar.gz --- (This appears to be a binary file; contents omitted.) --- NEW FILE: ffmpeg-for-2.2.5.tar.bz2 --- (This appears to be a binary file; contents omitted.) --- NEW FILE: faad2-2.7.tar.gz --- (This appears to be a binary file; contents omitted.) --- NEW FILE: faac-1.28.tar.gz --- (This appears to be a binary file; contents omitted.) --- NEW FILE: ffmpeg-sage-7.0.13-and-segmenter-2.2.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: yasm-1.0.1.tar.gz --- (This appears to be a binary file; contents omitted.) --- NEW FILE: lame-3.98.4.tar.gz --- (This appears to be a binary file; contents omitted.) --- NEW FILE: xvidcore-bin.zip --- (This appears to be a binary file; contents omitted.) |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/bin/macos In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/bin/macos Added Files: ffmpeg Log Message: --- NEW FILE: ffmpeg --- (This appears to be a binary file; contents omitted.) |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/extlib In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/extlib Added Files: sagex-api-7.0.7.0.jar Log Message: --- NEW FILE: sagex-api-7.0.7.0.jar --- (This appears to be a binary file; contents omitted.) |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/servlet In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/src/sagex/streaming/servlet Added Files: MediaFileThumbnailServlet.java MediaFileServlet.java HTTPLiveStreamingSegmentServlet.java SageServlet.java HTTPLiveStreamingPlaylistServlet.java Log Message: --- NEW FILE: MediaFileServlet.java --- package sagex.streaming.servlet; import java.io.File; import java.io.OutputStream; import java.io.RandomAccessFile; import java.net.SocketException; import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.mortbay.jetty.EofException; import org.mortbay.log.Log; import sage.SageTV; import sagex.api.AiringAPI; import sagex.api.MediaFileAPI; /** * Copied from nielm's web server to eliminate dependency. It has been modified * (e.g. throttling removed) to get the core functionality working. */ public class MediaFileServlet extends SageServlet { public MediaFileServlet() { super(); } /* (non-Javadoc) * @see net.sf.sageplugins.webserver.SageServlet#doServletGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ protected void doServletGet(HttpServletRequest req, HttpServletResponse res) throws Exception { try { System.out.println("----------- MediaFileServlet begin request Headers ---------------"); Enumeration headers = req.getHeaderNames(); while (headers.hasMoreElements()) { String headerName = (String) headers.nextElement(); if ((headerName != null) && (!headerName.equals("Host")) && (!headerName.equals("Authorization"))) { System.out.println(headerName + ": " + req.getHeader(headerName)); } } System.out.println("----------- MediaFileServlet end request Headers ---------------"); Object mediaFile = super.getMediaFile(req); if (mediaFile == null) { throw new IllegalArgumentException("no MediaFileID passed"); } File files[] = (File[]) SageTV.api("GetSegmentFiles", new Object[] {mediaFile}); int filenum = 0; if (files.length > 1) { String filenum_str=req.getParameter("Segment"); if (filenum_str == null) { throw new IllegalArgumentException("Segmented file: requires Segment Number"); } try { filenum = Integer.parseInt(filenum_str); if ( filenum < 0 || filenum > files.length) { throw new NumberFormatException(); } } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid Segment Number: "+filenum_str); } } File file=files[filenum]; if (!file.exists()) { throw new IllegalArgumentException("File does not exist: "+files[filenum]); } if (!file.canRead()) { throw new IllegalArgumentException("File can not be read: "+files[filenum]); } boolean headOnly=false; if (req.getMethod().equalsIgnoreCase("get")) { headOnly = false; } else if (!req.getMethod().equalsIgnoreCase("head")) { headOnly = true; } else { res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); return; } res.setStatus(HttpServletResponse.SC_OK); boolean isRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); // Handle Connection: close // if ("close".equals(req.getHeader("Connection"))) // { // res.setHeader("Connection", "close"); // } // Handle If-Modified-Since. long lastMod = file.lastModified(); try { if (!super.CheckIfModifiedSince(req,lastMod)) { res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); headOnly = true; } } catch (IllegalArgumentException e) { log(e.toString()); } res.setContentType(getServletContext().getMimeType(file.getName())); long fileLength = file.length(); if (fileLength < Integer.MAX_VALUE) { res.setContentLength((int) fileLength); } else { res.setHeader("Content-Length", Long.toString(fileLength)); } res.setDateHeader("Last-modified", lastMod); if (headOnly) { // System.out.println("Headers only"); res.flushBuffer(); } else { res.setHeader("Accept-Ranges", "bytes"); // check range request long start = 0; long stop = fileLength - 1; // if currently recording file, handle length specially: if (isRecording && filenum == files.length - 1) { // estimate file size of scheduled recording // long startTime = MediaFileAPI.GetStartForSegment(mediaFile, filenum); long endTime = AiringAPI.GetScheduleEndTime(mediaFile); // long startTime=((Long)SageTV.api("GetStartForSegment",new Object[]{mediaFile,new Integer(filenum)})).longValue(); // long endTime=((Long)SageTV.api("GetScheduleEndTime",new Object[]{mediaFile})).longValue(); long currTime = System.currentTimeMillis(); long byterate = (fileLength)/(currTime-startTime); fileLength = ((endTime-startTime) * 105 / 100) * byterate; stop = fileLength - 1; Log.debug("MediaFileServlet: Currently recording file: start="+startTime+" curr="+currTime+" end="+endTime+" est filesize="+fileLength+ " est byterate="+byterate); } String range = req.getHeader("Range"); if (range != null && range.trim().startsWith("bytes=")) { Log.debug("MediaFileServlet: Requested Range = " + range + " File length =" + fileLength); range = range.trim().substring(6); String ranges[] = range.split(","); if (ranges.length > 0) { String startstop[] = ranges[0].split("-"); if (startstop.length > 0 && startstop[0].trim().length() > 0) { try { start = Long.parseLong(startstop[0].trim()); } catch (NumberFormatException e) { log("invalid range specifyer (start)"+range,e); } } if (startstop.length > 1 && startstop[1].trim().length() > 0) { try { stop = Long.parseLong(startstop[1].trim()); } catch (NumberFormatException e) { log("invalid range specifyer (stop)"+range,e); } } // if currently recording file, handle range request specially: if (isRecording && filenum == files.length - 1) { // check start point if (start > fileLength) { start = fileLength; } } // check ranges if (start > stop || start < 0 || stop >= fileLength) { Log.debug("MediaFileServlet: request range is out of range."); res.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); } } // System.out.println("MediaFileServlet: Supplying range bytes "+Long.toString(start)+"-"+Long.toString(stop)+"/"+Long.toString(fileLength)); // System.out.println("MediaFileServlet: Debug enabled = " + Log.getLog().isDebugEnabled()); // System.out.println("MediaFileServlet: Log class = " + Log.getLog().getClass().getName()); Log.debug("MediaFileServlet: Supplying range bytes "+Long.toString(start)+"-"+Long.toString(stop)+"/"+Long.toString(fileLength)); RandomAccessFile rfile = new RandomAccessFile(file, "r"); res.setHeader("Content-Range", "bytes " + Long.toString(start) + "-" + Long.toString(stop) + "/" + Long.toString(fileLength)); res.setHeader("Content-Length", Long.toString(stop-start+1)); //res.setHeader("Content-Disposition","attachment; filename="+file.getName()); OutputStream os = res.getOutputStream(); try { if (start > 0) { rfile.seek(start); } // copy stream stopping at 'stop' bytes, or at EOF if stop==fileLen-1 long nextByte = start; int buflen = 1024; int readLen = -1; byte[] buf = new byte[buflen]; while (stop == fileLength - 1 || nextByte <= stop) { readLen = rfile.read(buf, 0, buflen); if (readLen == -1) { // EOF reached -- check if file is still being recorded. isRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); if (isRecording) { files = MediaFileAPI.GetSegmentFiles(mediaFile); if (filenum == files.length - 1) { // still recording this segment -- try reading again after a few 100ms Thread.sleep(500); readLen = rfile.read(buf, 0, buflen); } } } if (readLen > 0) { long bytesRemaining = stop - nextByte + 1; long writeLen = Math.min(readLen, bytesRemaining); try {// TODO retries os.write(buf, 0, (int) writeLen); } catch (EofException e) { throw e; } catch (Throwable t) { t.printStackTrace(); throw t; } nextByte += writeLen; } else { Log.debug("MediaFileServlet: EOF met"); // end of file -- stop break; } } } catch (SocketException e) { /* assume socket has been closed -- ignore */ } finally { try { rfile.close(); } catch (Exception e) { // ignore } try { os.close(); } catch (Exception e) { // ignore } } } } catch (Throwable e) { e.printStackTrace(); } } } --- NEW FILE: HTTPLiveStreamingSegmentServlet.java --- package sagex.streaming.servlet; import java.io.IOException; import java.util.Enumeration; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.mortbay.log.Log; import sagex.streaming.httpls.segment.SegmentManager; import sagex.streaming.httpls.segment.SegmentProducer; import sagex.streaming.listener.AppContextListener; @SuppressWarnings("serial") public class HTTPLiveStreamingSegmentServlet extends SageServlet { @Override protected void doServletGet(HttpServletRequest req, HttpServletResponse resp) throws Exception { /* TODO Log.info*/System.out.println("HTTPLiveStreamingSegmentServlet: Request started"); Object mediaFile = super.getMediaFile(req); if (mediaFile == null) { throw new IllegalArgumentException("No MediaFileId passed"); } System.out.println("----------- HTTPLiveStreamingSegmentServlet begin request Headers ---------------"); Enumeration headers = req.getHeaderNames(); while (headers.hasMoreElements()) { String headerName = (String) headers.nextElement(); if ((headerName != null) && (!headerName.equals("Host")) && (!headerName.equals("Authorization"))) { System.out.println(headerName + ": " + req.getHeader(headerName)); } } System.out.println("----------- HTTPLiveStreamingSegmentServlet end request Headers ---------------"); // File file = MediaFileAPI.GetFileForSegment(mediaFile, 0); // TODO media file segment resp.setHeader("Accept-Ranges", "none"); // resp.setHeader("Content-Length", String.valueOf(Long.MAX_VALUE)); String range = req.getHeader("Range"); if (range != null && range.equals("bytes=0-1".trim())) { System.out.println("Writing 2 bytessss"); // resp.setStatus(416); // Range not satisfiable resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // resp.sendRedirect("/sagem/m/TranscodedMediaFile"); // resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); // resp.setHeader("location", "http://friesen.homelinux.com:8080/sagem/m/TranscodedMediaFile"); // resp.setHeader("Location", "http://friesen.homelinux.com:8080/sagem/m/TranscodedMediaFile"); // resp.setHeader("Content-Location", "http://friesen.homelinux.com:8080/sagem/m/TranscodedMediaFile"); // resp.setHeader("Content-Type", "text/html"); // resp.setContentType(getServletContext().getMimeType(file.getAbsolutePath())); // resp.setHeader("Content-Length", "0"); // resp.setHeader("Content-Range", "0-1/*"); // resp.setHeader("Content-Range", "bytes 0-1/2"); resp.setHeader("Content-Range", "bytes 0-1/2000000"); // resp.setHeader("Content-Range", "bytes 0-1/" + sp.getNextSegmentLength()); resp.setHeader("Content-Length", "2"); resp.getOutputStream().write(0); resp.getOutputStream().write(0); resp.getOutputStream().flush(); } else { String conversionId = req.getParameter("ConversionId"); if ((conversionId == null) || (conversionId.trim().length() == 0)) { /* TODO Log.info*/System.out.println("ConversionId request parameter is required"); resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String sequenceString = req.getParameter("Sequence"); String qualityString = req.getParameter("Quality"); String mediaFileIdString = req.getParameter("MediaFileId"); String mediaFileSegmentString = req.getParameter("MediaFileSegment"); // optional, default is zero int sequence = 0; int mediaFileId = 0; int mediaFileSegment = 0; try { sequence = Integer.parseInt(sequenceString); } catch (NumberFormatException e) { /* TODO Log.info*/System.out.println("Sequence request parameter is invalid"); // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } try { mediaFileId = Integer.parseInt(mediaFileIdString); } catch (NumberFormatException e) { /* TODO Log.info*/System.out.println("MediaFileId or AiringId request parameter is invalid"); // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } try { if (mediaFileSegmentString == null) { mediaFileSegment = 0; } else { mediaFileSegment = Integer.parseInt(mediaFileSegmentString); } } catch (NumberFormatException e) { /* TODO Log.info*/System.out.println("MediaFileId or AiringId request parameter is invalid"); // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } resp.setStatus(HttpServletResponse.SC_OK); // resp.setContentType(getServletContext().getMimeType(file.getAbsolutePath())); // resp.setContentType("video/mp4"); resp.setContentType("video/MP2T"); // resp.setHeader("Content-Length", String.valueOf(sp.getNextSegmentLength()/*file.length()*/)); // TODO get from SegmentGobbler ServletContext sc = getServletContext(); SegmentManager sm = (SegmentManager) sc.getAttribute(AppContextListener.SEGMENT_MANAGER_ATTRIBUTE_NAME); SegmentProducer segmentProducer = null; synchronized (sm) { segmentProducer = sm.getSegmentProducer(conversionId); if (segmentProducer == null) { segmentProducer = new SegmentProducer(conversionId, req.getHeader("User-Agent")); sm.addSegmentProducer(conversionId, segmentProducer); } } synchronized (segmentProducer) { // TODO only call stream next segment, encapsulate the rest if (!segmentProducer.nextState(sequence, qualityString, mediaFileId, mediaFileSegment) /*|| (sp == null)*/) { try { segmentProducer.reset(); } catch (IOException e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } // System.out.println("HTTPLiveStreamingSegmentServlet: mime type " + getServletContext().getMimeType(f.getAbsolutePath())); } long len = segmentProducer.getNextSegmentLength(); System.out.println("Content-Length: " + len); resp.setHeader("Content-Length", Long.toString(len)); segmentProducer.streamNextSegment(resp.getOutputStream()); } } resp.flushBuffer(); System.out.println("Request ended"); } // choose transcoder // transcode file // use time range // return mime type video/MP2T } --- NEW FILE: MediaFileThumbnailServlet.java --- package sagex.streaming.servlet; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.OutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import sage.SageTV; /** * Copied from nielm's web server to eliminate dependency. */ public class MediaFileThumbnailServlet extends SageServlet { private static final long serialVersionUID = 5914576130809217889L; protected void doServletGet(HttpServletRequest req, HttpServletResponse resp) throws Exception { try { Object thumb = null; String albumName = req.getParameter("albumname"); if (albumName == null) { Object mediaFile = super.getMediaFile(req); if (mediaFile != null) { thumb = SageTV.api("GetThumbnail", new Object[] {mediaFile}); if (thumb == null) { throw new Exception("GetThumbnail returned null"); } } } else { // album thumb String artist = req.getParameter("artist"); if (artist == null) artist = ""; String year = req.getParameter("year"); if (year == null) year = ""; String genre = req.getParameter("genre"); if (genre == null) genre = ""; Object albums = SageTV.api("GetAlbums", null); albums = SageTV.api("FilterByMethod",new Object[] {albums,"GetAlbumName",albumName,Boolean.TRUE}); albums = SageTV.api("FilterByMethod",new Object[] {albums,"GetAlbumArtist",artist,Boolean.TRUE}); albums = SageTV.api("FilterByMethod",new Object[] {albums,"GetAlbumYear",year,Boolean.TRUE}); albums = SageTV.api("FilterByMethod",new Object[] {albums,"GetAlbumGenre",genre,Boolean.TRUE}); Object album = SageTV.api("GetElement", new Object[] {albums,0}); if (album == null) { throw new Exception("Cound not find album for: " + albumName + "/" + artist + "/" + year + "/" + genre); } if ((Boolean) SageTV.api("HasAlbumArt", new Object[] {album})) { thumb = SageTV.api("GetAlbumArt", new Object[] {album}); } if (thumb == null) { throw new Exception("GetAlbumArt on album: " + album + " returned null"); } } BufferedImage image = (BufferedImage) SageTV.api("GetImageAsBufferedImage", new Object[] {thumb}); if (image == null) { throw new Exception("GetImageAsBufferedImage returned null"); } // got a BufferedImage, write it out as JPEG // cache for at least 10 mins boolean headOnly = false; long lastMod = System.currentTimeMillis() - (10*60*1000); try { if (!super.CheckIfModifiedSince(req,lastMod)) { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); headOnly = true; } } catch (IllegalArgumentException e) { e.printStackTrace(); } resp.setContentType("image/png"); resp.setDateHeader("Last-modified", lastMod); resp.setBufferSize(8192); // set expiry date to now+1 week long expiry = System.currentTimeMillis() + (1000*60*60*24*7); resp.setDateHeader("Expires", expiry); if (!headOnly) { if (req.getParameter("small") != null && (image.getWidth() > 100 || image.getHeight() > 100)) { Image scaledimage = image.getScaledInstance(50, -1, Image.SCALE_SMOOTH); image = new BufferedImage(scaledimage.getWidth(null), scaledimage.getHeight(null), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = image.createGraphics(); g2d.drawImage(scaledimage, 0, 0, null); g2d.dispose(); } OutputStream os = resp.getOutputStream(); javax.imageio.ImageIO.write(image, "png", os); os.close(); } } catch (Exception e) { e.printStackTrace(); } } } --- NEW FILE: HTTPLiveStreamingPlaylistServlet.java --- package sagex.streaming.servlet; import java.util.Enumeration; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.mortbay.log.Log; import sagex.api.MediaFileAPI; @SuppressWarnings("serial") public class HTTPLiveStreamingPlaylistServlet extends SageServlet { public static final int TARGET_DURATION = 5; // TODO variant playlist bitrates and params // private static String[] VARIANT_PLAYLIST_BITRATES = {"256", "1536", "1280", "1024", "768", "512", "384"}; // private static String[] VARIANT_PLAYLIST_BITRATES = {"150", "1240", "840", "640", "440", "240"}; private static String[] VARIANT_PLAYLIST_BITRATES = {"150", "1240", "840", "640", "440", "240"}; public HTTPLiveStreamingPlaylistServlet() { } @Override protected void doServletGet(HttpServletRequest req, HttpServletResponse resp) throws Exception { Object mediaFile = super.getMediaFile(req); if (mediaFile == null) { throw new IllegalArgumentException("No MediaFileID passed"); } System.out.println("----------- HTTPLiveStreamingPlaylistServlet begin request Headers ---------------"); Enumeration headers = req.getHeaderNames(); while (headers.hasMoreElements()) { String headerName = (String) headers.nextElement(); if ((headerName != null) && (!headerName.equals("Host")) && (!headerName.equals("Authorization"))) { System.out.println(headerName + ": " + req.getHeader(headerName)); } } System.out.println("----------- HTTPLiveStreamingPlaylistServlet end request Headers ---------------"); // resp.setContentType("application/x-mpegURL"); resp.setContentType("application/vnd.apple.mpegurl"); resp.setHeader("Accept-Ranges", "none"); String conversionId = req.getParameter("ConversionId"); String playlist = ""; if (conversionId == null) { playlist = createVariantPlaylist(req); } else { playlist = createSegmentPlaylist(req); } byte[] playlistBytes = playlist.getBytes("UTF-8"); resp.setHeader("Content-Length", String.valueOf(playlistBytes.length)); resp.setHeader("Cache-Control", "none"); resp.setHeader("Pragma", "no-cache"); // TODO how should integer overflow be handled try { // TODO ByteBuffer // TODO EofException // resp.getWriter().print(sb.toString()); // resp.getWriter().flush(); // for (long i = startByte; i < byteCount; i += 1) // { // System.out.println("i: " + i); // resp.getOutputStream().write(sbBytes, (int) i/*startByte*/, (int) Math.min(1, byteCount - i + 1)/*byteCount*/); // } resp.getOutputStream().write(playlistBytes); resp.getOutputStream().flush(); } catch (Exception e) { // Log.info(e.getMessage()); // Log.ignore(e); e.printStackTrace(System.out); resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } finally { System.out.println("Chars: " + playlist.length() + ", Bytes: " + playlistBytes.length); } } private String createVariantPlaylist(HttpServletRequest req) { Object mediaFile = super.getMediaFile(req); if (mediaFile == null) { throw new IllegalArgumentException("No MediaFileID passed"); } int mediaFileId = MediaFileAPI.GetMediaFileID(mediaFile); StringBuilder sb = new StringBuilder(); String url = null; String conversionId = UUID.randomUUID().toString(); // Header sb.append("#EXTM3U\r\n"); System.out.println("#EXTM3U\r\n"); for (String bitrate : VARIANT_PLAYLIST_BITRATES) { sb.append("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate + "000\r\n"); System.out.println("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate + "000\r\n"); url = ("/stream/HTTPLiveStreamingPlaylist?" + "MediaFileId=" + mediaFileId + "&ConversionId=" + conversionId + "&Quality=" + bitrate + "\r\n"); sb.append(url); System.out.print(url); } return sb.toString(); // #EXTM3U // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=200000 // gear1/prog_index.m3u8 // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=311111 // gear2/prog_index.m3u8 // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=484444 // gear3/prog_index.m3u8 // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=737777 // gear4/prog_index.m3u8 } private String createSegmentPlaylist(HttpServletRequest req) { Object mediaFile = super.getMediaFile(req); if (mediaFile == null) { throw new IllegalArgumentException("No MediaFileID passed"); } String conversionId = req.getParameter("ConversionId"); String quality = req.getParameter("Quality"); String showName = MediaFileAPI.GetMediaTitle(mediaFile); int numberOfMediaFileSegments = MediaFileAPI.GetNumberOfSegments(mediaFile); boolean isRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); StringBuilder sb = new StringBuilder(); System.out.println("MediaFileId = " + req.getParameter("MediaFileId")); System.out.println("numberOfMediaFileSegments = " + numberOfMediaFileSegments); System.out.println("showName = " + showName); sb.append("#EXTM3U\r\n"); System.out.println("#EXTM3U\r\n"); sb.append("#EXT-X-TARGETDURATION:" + TARGET_DURATION + "\r\n"); System.out.println("#EXT-X-TARGETDURATION:" + TARGET_DURATION + "\r\n"); // sb.append("#EXT-X-MEDIA-SEQUENCE:0\r\n"); // System.out.println("#EXT-X-MEDIA-SEQUENCE:0\r\n"); for (int currentMediaFileSegment = 0; currentMediaFileSegment < numberOfMediaFileSegments; currentMediaFileSegment++) { // milliseconds long mediaFileSegmentDuration = MediaFileAPI.GetDurationForSegment(mediaFile, currentMediaFileSegment); System.out.println("segmentDuration = " + mediaFileSegmentDuration); mediaFileSegmentDuration = mediaFileSegmentDuration / 1000; // milliseconds to seconds int sequence = 0; // httpls segment sequence number for (int i = 0; i < mediaFileSegmentDuration; i += TARGET_DURATION) { long currentDuration = Math.min(TARGET_DURATION, mediaFileSegmentDuration - i); sb.append("#EXTINF:" + currentDuration + "," + showName + "\r\n"); System.out.println("#EXTINF:" + currentDuration + "," + showName + "\r\n"); // each segment has header and that causes problems with iphone // start frame hh:mm:ss:ff // hh:mm:ss:00 // duration hh:mm:ss:ff // 00:00:10:00 // sb.append("/sagem/m/TranscodedHTTPLiveStreamingMediaFile?s=" + i + "&d=" + currentDuration + "&MediaFileId=" + req.getParameter("MediaFileId") + "\r\n"); // System.out.print("/sagem/m/TranscodedHTTPLiveStreamingMediaFile?s=" + i + "&d=" + currentDuration + "&MediaFileId=" + req.getParameter("MediaFileId") + "\r\n"); String url = "/stream/HTTPLiveStreamingSegment?" + "Sequence=" + sequence + "&MediaFileId=" + req.getParameter("MediaFileId") + "&ConversionId=" + conversionId + "&Quality=" + quality + "&MediaFileSegment=" + currentMediaFileSegment + "\r\n"; sb.append(url); System.out.print(url); sequence++; } } // #EXT-X-MEDIA-SEQUENCE:<number> // #EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ> // #EXT-X-ALLOW-CACHE:<YES|NO> // #EXT-X-STREAM-INF if (!isRecording) { sb.append("#EXT-X-ENDLIST\r\n"); System.out.println("#EXT-X-ENDLIST\r\n"); } return sb.toString(); } } --- NEW FILE: SageServlet.java --- package sagex.streaming.servlet; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import sage.SageTV; /** * Core sage servlet with helper functions */ public abstract class SageServlet extends HttpServlet { public void log(String arg0) { if (!arg0.equals("init") && !arg0.equals("destroy")) { super.log(arg0); } } protected static String charset = null; protected SageServlet() { } @Override public void init() throws ServletException { super.init(); try { charset = (String) SageTV.api("GetProperty", new Object[] {"nielm/webserver/charset", "UTF-8"}); } catch (InvocationTargetException e) { System.out.println("Error getting charset:" + e.toString()); System.out.println("caused:" + e.getCause().toString()); e.printStackTrace(System.out); charset = "UTF-8"; } } protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(charset); try { doServletGet(req,resp); } catch (Exception e) { throw new ServletException(e.getMessage(), e); } } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } protected abstract void doServletGet(HttpServletRequest req, HttpServletResponse resp) throws Exception; protected Object getMediaFile(HttpServletRequest req) { Object mediaFile = null; try { String idStr = req.getParameter("MediaFileId"); if (idStr != null && ! idStr.equals("")) { Integer id = Integer.parseInt(idStr); mediaFile = SageTV.api("GetMediaFileForID", new Object[] {id}); } else { idStr = req.getParameter("AiringId"); if (idStr != null && ! idStr.equals("")) { Integer id = Integer.parseInt(idStr); Object airing = SageTV.api("GetAiringForID", new Object[] {id}); if (airing != null) { mediaFile = SageTV.api("GetMediaFileForAiring", new Object[] {airing}); } } } } catch (Exception e) { e.printStackTrace(); } return mediaFile; } /// Check If-Modified-Since header // returns True if header is older than lastModified -- // and file needs to be sent protected boolean CheckIfModifiedSince(HttpServletRequest req, long lastModified) throws IllegalArgumentException { long ifModSince = req.getDateHeader("If-Modified-Since"); if (ifModSince != -1) { // headers are to nearest second, timestamps are // often of a higher resolution... lastModified = (lastModified / 1000) * 1000; if (ifModSince >= lastModified) { return false; } } return true; } } |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/bin/win32 In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/bin/win32 Added Files: ffmpeg.exe pthreadGC2.dll Log Message: --- NEW FILE: pthreadGC2.dll --- (This appears to be a binary file; contents omitted.) --- NEW FILE: ffmpeg.exe --- (This appears to be a binary file; contents omitted.) |
From: jreichen <jre...@us...> - 2010-11-03 03:42:37
|
Update of /cvsroot/sageplugins/MediaStreaming/resources/plugin In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4766/resources/plugin Added Files: plugin.xml Log Message: --- NEW FILE: plugin.xml --- <?xml version="1.0" encoding="UTF-8"?> <SageTVPlugin> <Name>Media Streaming Services</Name> <Identifier>mediastreaming</Identifier> <Description>Provides HTTP Live Streaming services to the Mobile Web UI. Used for viewing SageTV recordings on iPhone, iPod, and iPad. It can be extended by plugin developers to support other clients and audio/video formats.</Description> <Author>jreichen</Author> <CreationDate>2010.10.13</CreationDate> <ModificationDate>@@last-modified@@</ModificationDate> <Version>@@plugin-impl-version@@</Version> <Desktop>false</Desktop> <ResourcePath>mediastreaming</ResourcePath> <!--Screenshot title="Main Menu">http://forums.sagetv.com/forums/downloads/mobile_menu_f3J.png</Screenshot> <Webpage title="Forum Support Thread">http://forums.sagetv.com/forums/showthread.php?t=48460</Webpage> <Webpage title="Mobile Web Interface Wiki">http://tools.assembla.com/sageplugins/wiki/MobileWebInterface</Webpage--> <ReleaseNotes> <![CDATA[ Linux Users: Currently libfaac must be installed separately. This will hopefully be fixed soon. Mac OS X Users: This plugin is designed to support Mac OS X. It cannot be tested until SageTV releases a Mac OS X build of v7. I don't own a SageTV Mac OS X license so I may rely on users to test it. Version 1.0.0 1. Real-time variable bitrate transcoding of SageTV recordings to iPhone, iPod touch, and iPad devices. ]]> </ReleaseNotes> <Dependency> <Core></Core> <MinVersion>7.0.16</MinVersion> </Dependency> <Dependency> <JVM></JVM> <MinVersion>1.5</MinVersion> </Dependency> <Dependency> <Plugin>Jetty</Plugin> <MinVersion>2.0.1</MinVersion> </Dependency> <PluginType>Standard</PluginType> <Package> <PackageType>System</PackageType> <Location>@@download-url-location@@/mediastreaming-war-file-@@plugin-impl-version@@.zip@@download-url-params@@</Location> <MD5>@@war-file-checksum@@</MD5> </Package> <!-- context file must be installed after the war file --> <Package> <PackageType>System</PackageType> <Location>@@download-url-location@@/mediastreaming-context-file-@@plugin-impl-version@@.zip@@download-url-params@@</Location> <MD5>@@context-file-checksum@@</MD5> </Package> <ImplementationClass></ImplementationClass> </SageTVPlugin> |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/servlet In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/src/sagex/streaming/servlet Log Message: Directory /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/servlet added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/ffmpeg/packages In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/ffmpeg/packages Log Message: Directory /cvsroot/sageplugins/MediaStreaming/ffmpeg/packages added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/io In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/src/sagex/streaming/io Log Message: Directory /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/io added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/bin In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/bin Log Message: Directory /cvsroot/sageplugins/MediaStreaming/bin added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/src/sagex/streaming Log Message: Directory /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF/lib In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/WebContent/WEB-INF/lib Log Message: Directory /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF/lib added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/WebContent Log Message: Directory /cvsroot/sageplugins/MediaStreaming/WebContent added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/resources/contexts In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/resources/contexts Log Message: Directory /cvsroot/sageplugins/MediaStreaming/resources/contexts added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF/ffmpeg In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/WebContent/WEB-INF/ffmpeg Log Message: Directory /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF/ffmpeg added to the repository |
From: jreichen <jre...@us...> - 2010-11-03 03:22:02
|
Update of /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv4611/WebContent/WEB-INF Log Message: Directory /cvsroot/sageplugins/MediaStreaming/WebContent/WEB-INF added to the repository |