From: jreichen <jre...@us...> - 2011-01-15 21:26:12
|
Update of /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/httpls/segment In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv23748/src/sagex/streaming/httpls/segment Modified Files: SegmenterProcess.java Log Message: - Use SageTV's ffmpeg for transcoding - Fixed problems watching shows currently being recorded - Added more logging detail Index: SegmenterProcess.java =================================================================== RCS file: /cvsroot/sageplugins/MediaStreaming/src/sagex/streaming/httpls/segment/SegmenterProcess.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** SegmenterProcess.java 5 Nov 2010 02:58:36 -0000 1.2 --- SegmenterProcess.java 15 Jan 2011 21:26:03 -0000 1.3 *************** *** 8,12 **** --- 8,14 ---- import java.io.OutputStreamWriter; import java.lang.Thread.UncaughtExceptionHandler; + import java.util.ArrayList; import java.util.HashMap; + import java.util.List; import java.util.Map; import java.util.concurrent.CancellationException; *************** *** 19,34 **** 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 */ --- 21,44 ---- import sagex.api.MediaFileAPI; + import sagex.streaming.httpls.playlist.SegmentPlaylist; import sagex.streaming.io.SegmentInputStream; import sagex.streaming.io.StreamGobbler; public class SegmenterProcess implements UncaughtExceptionHandler { /** ! * The ffmepg executable used for transcoding audio and video. */ ! private static File transcoderFile; ! /** ! * The executable used for segmenting audio and video. ! */ ! private static File segmenterFile; private static final String SEGMENTER_PARAM = "segmenter"; /** + * The command that tells the transcoder when the input file has stopped recording + */ + private String RECORDING_STOPPED = "inactivefile\r\n"; + /** * A thread pool that allows the stream gobblers to reuse threads if available */ *************** *** 48,53 **** */ private SegmentInputStream segmentInputStream = null; ! private double currentTime = 0; ! private StreamGobbler stdoutTranscoderGobbler = null; private StreamGobbler stderrTranscoderGobbler = null; --- 58,70 ---- */ private SegmentInputStream segmentInputStream = null; ! /** ! * ! */ ! private File inputFile = null; ! private boolean isCurrentlyRecording = false; ! /** ! * Input and output streams for transcoder and segmenter processes ! */ ! private OutputStream stdinTranscoder = null; private StreamGobbler stdoutTranscoderGobbler = null; private StreamGobbler stderrTranscoderGobbler = null; *************** *** 58,62 **** * Set the location of the ffmpeg executable when the web app starts */ ! public static void setProcessLocation(File f) { if (!f.exists()) --- 75,79 ---- * Set the location of the ffmpeg executable when the web app starts */ ! public static void setTranscoderProcessLocation(File f) { if (!f.exists()) *************** *** 65,70 **** } ! Log.info("SegmenterProcess for HTTPLS web streaming is " + f.getAbsolutePath()); ! ffmpegFile = f; } --- 82,101 ---- } ! Log.info("SegmenterProcess transcoder for HTTPLS web streaming is " + f.getAbsolutePath()); ! transcoderFile = f; ! } ! ! /** ! * Set the location of the ffmpeg executable when the web app starts ! */ ! public static void setSegmenterProcessLocation(File f) ! { ! if (!f.exists()) ! { ! throw new IllegalArgumentException("File does not exist."); ! } ! ! Log.info("SegmenterProcess segmenter for HTTPLS web streaming is " + f.getAbsolutePath()); ! segmenterFile = f; } *************** *** 77,82 **** try { double startTime = sequenceToStartTime(sequence); - currentTime = startTime; // get builders for the transcoding and segmenting processes --- 108,114 ---- try { + this.inputFile = inputFile; + double startTime = sequenceToStartTime(sequence); // get builders for the transcoding and segmenting processes *************** *** 88,91 **** --- 120,126 ---- segmenterProcess = segmenterProcessBuilder.start(); + // tell the transcoder if and when recording has stopped + stdinTranscoder = transcoderProcess.getOutputStream(); + // copy the transcoder's a/v output to the segmenter // if this gobbler exits, streaming cannot continue so the streams should be closed *************** *** 116,120 **** interrupt(stderrTranscoderGobbler); - Log.debug("SegmenterProcess: stop transcoder process"); if (transcoderProcess != null) { --- 151,154 ---- *************** *** 127,130 **** --- 161,165 ---- { // process has not terminated + Log.debug("SegmenterProcess: stop transcoder process"); OutputStream os = transcoderProcess.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); *************** *** 193,202 **** // reset variables - currentTime = 0; - transcoderProcess = null; segmenterProcess = null; segmentInputStream = null; stdoutTranscoderGobbler = null; stderrTranscoderGobbler = null; --- 228,236 ---- // reset variables transcoderProcess = null; segmenterProcess = null; segmentInputStream = null; + stdinTranscoder = null; stdoutTranscoderGobbler = null; stderrTranscoderGobbler = null; *************** *** 252,256 **** public void streamNextSegment(OutputStream os) throws Exception { ! currentTime += HTTPLiveStreamingPlaylistServlet.TARGET_DURATION; try --- 286,307 ---- public void streamNextSegment(OutputStream os) throws Exception { ! if (isCurrentlyRecording) ! { ! // TODO move to getNextSegmentLength ! Object mediaFile = MediaFileAPI.GetMediaFileForFilePath(inputFile); ! isCurrentlyRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); ! ! if (isCurrentlyRecording) ! { ! Log.debug("inputFile " + inputFile.getName() + " is still recording"); ! } ! else ! { ! Log.debug("inputFile " + inputFile.getName() + " has finished recording. Sending message to transcoder"); ! stdinTranscoder.write(RECORDING_STOPPED.getBytes("US-ASCII")); ! stdinTranscoder.flush(); ! } ! } ! try *************** *** 297,300 **** --- 348,352 ---- Log.debug("Killing transcoder process"); stopProcess(); + // TODO not necessarily throw new Exception("SegmenterProcess: segmenter unexpectedly exited"); } *************** *** 317,332 **** 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")); ! ! Log.debug("numAudioStreams " + numAudioStreams); ! ! for (int i = 0; i < numAudioStreams; i++) ! { ! Integer streamIndex = Integer.parseInt(MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Audio." + i + ".Index")); ! Log.debug("Audio Stream " + i + ", Index = " + streamIndex); ! } Map<String, String[]> qualityMap = new HashMap<String, String[]>(); --- 369,384 ---- 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")); ! // ! // Log.debug("numAudioStreams " + numAudioStreams); ! // ! // for (int i = 0; i < numAudioStreams; i++) ! // { ! // Integer streamIndex = Integer.parseInt(MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Audio." + i + ".Index")); ! // Log.debug("Audio Stream " + i + ", Index = " + streamIndex); ! // } Map<String, String[]> qualityMap = new HashMap<String, String[]>(); *************** *** 384,443 **** } - 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", ! "-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, ! "-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; } --- 436,441 ---- } ProcessBuilder pb = new ProcessBuilder(); ! pb.command(getTranscoderParams(inputFile, startTime, currentQuality)); return pb; } *************** *** 446,456 **** { 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) { --- 444,526 ---- { ProcessBuilder pb = new ProcessBuilder(); ! pb.command(segmenterFile.getPath(), SEGMENTER_PARAM, ! Integer.toString(SegmentPlaylist.TARGET_DURATION), Long.toString(sequence)); return pb; } + private List<String> getTranscoderParams(File inputFile, double startTime, String[] currentQuality) + { + List<String> params = new ArrayList<String>(); + + Object mediaFile = MediaFileAPI.GetMediaFileForFilePath(inputFile); + String aspect = MediaFileAPI.GetMediaFileMetadata(mediaFile, "Format.Video.Aspect"); + isCurrentlyRecording = MediaFileAPI.IsFileCurrentlyRecording(mediaFile); + + String cmd = transcoderFile.getAbsolutePath(); + + ProcessBuilder pb = new ProcessBuilder(); + params.add(cmd); + if (isCurrentlyRecording) + { + params.add("-activefile"); + params.add("-stdinctrl"); + } + params.add("-threads"); params.add(Integer.toString(Runtime.getRuntime().availableProcessors()));//"4", + params.add("-flags2"); params.add("+fast"); + params.add("-flags"); params.add("+loop"); + params.add("-g"); params.add("30"); + params.add("-keyint_min"); params.add("1"); + params.add("-bf"); params.add("0"); + params.add("-b_strategy"); params.add("0"); + params.add("-flags2"); + params.add("-wpred-dct8x8"); + params.add("-cmp"); params.add("+chroma"); + params.add("-deblockalpha"); params.add("0"); + params.add("-deblockbeta"); params.add("0"); + params.add("-refs"); params.add("1"); + params.add("-coder"); params.add("0"); + params.add("-me_range"); params.add("16"); + params.add("-subq"); params.add("5"); + params.add("-partitions"); params.add("+parti4x4+parti8x8+partp8x8"); + params.add("-trellis"); params.add("0"); + params.add("-sc_threshold"); params.add("40"); + params.add("-i_qfactor"); params.add("0.71"); + params.add("-qcomp"); params.add("0.6"); + // "-map", "0.0:0.0", + // "-map", "0.1:0.1", + // "-map", "0.1:0.0", + // "-map", "0.0:0.1", + params.add("-ss"); params.add(Double.toString(startTime)); + params.add("-i"); params.add(inputFile.getAbsolutePath()); + // "-cropleft", "0", + // "-cropright", "0", + // "-croptop", "0", + // "-cropbottom", "0", + params.add("-s"); params.add(currentQuality[0]); + params.add("-aspect"); params.add(aspect); + params.add("-y"); + params.add("-f"); params.add("mpegts"); + params.add("-async"); params.add("1"); + params.add("-vcodec"); params.add("libx264"); + params.add("-level"); params.add("30"); + params.add("-bufsize"); params.add("512k"); + params.add("-b"); params.add(currentQuality[1]); + params.add("-bt"); params.add(currentQuality[2]); + params.add("-qmax"); params.add("48"); + params.add("-qmin"); params.add("2"); + params.add("-r"); params.add("29.97"); + // "-acodec", "libmp3lame", + params.add("-vol"); params.add("1024"); + params.add("-acodec"); params.add("libfaac"); + params.add("-ab"); params.add(currentQuality[4]); + params.add("-ar"); params.add(currentQuality[5]); + params.add("-ac"); params.add("2"); + params.add("-"); + + return params; + } + private long parseSequence(String sequence) { *************** *** 480,493 **** 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(); --- 550,563 ---- private double sequenceToStartTime(long sequence) { ! return sequence * SegmentPlaylist.TARGET_DURATION; } public static void main(String[] args) throws IOException { ! setTranscoderProcessLocation(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(transcoderFile.getAbsolutePath()); SegmenterProcess sp = new SegmenterProcess(); |