public override Stream ProvideCustomActionFile(string action, string param) { if (action == "playlist") { WCFUtil.SetContentType("application/vnd.apple.mpegurl"); string playlistPath = Path.Combine(TemporaryDirectory, "index.m3u8"); if (!File.Exists(playlistPath)) { StreamLog.Warn(Identifier, "HTTPLiveStreamer: Client requested index.m3u8 that doesn't exist for identifier '{0}'", Identifier); return(Stream.Null); } // FFMpeg outputs the local segment paths in the playlist, replace with url string playlist = ReplacePathsWithURLs(playlistPath); if (playlist == string.Empty) { // The playlist file is empty until the first segment has finished being encoded, // wait for next segment to begin and retry. string segmentPath = Path.Combine(TemporaryDirectory, "000001.ts"); while (!File.Exists(segmentPath)) { System.Threading.Thread.Sleep(100); } playlist = ReplacePathsWithURLs(playlistPath); } return(new MemoryStream(Encoding.UTF8.GetBytes(playlist))); } return(base.ProvideCustomActionFile(action, param)); }
private void InfoTimerTick(object source, ElapsedEventArgs args) { while (true) { try { // let's ignore the time here, for reasons detailed in VLCWrapperParsingUnit.cs around line 115 float position = transcoder.GetPosition(); StreamLog.Trace(context.Identifier, "VLCManagedInfo: calling NewPercentage with position {0}", position); calculator.NewPercentage(position); calculator.SaveStats(infoReference); } catch (Exception ex) { StreamLog.Warn(context.Identifier, "Failed to get VLC data", ex); } } }
public bool Start() { // wait for the input pipe to be ready if (transcoderInputStream is NamedPipe) { ((NamedPipe)transcoderInputStream).WaitTillReady(); } // copy the inputStream to the transcoderInputStream if (doInputCopy) { StreamLog.Info(context.Identifier, "Encoding: Copy stream of type {0} into transcoder input stream of type {1}", InputStream.ToString(), transcoderInputStream.ToString()); StreamCopy.AsyncStreamCopy(InputStream, transcoderInputStream, "transinput"); } // delay start of next unit till our output stream is ready if (DataOutputStream is NamedPipe && (outputMethod == TransportMethod.NamedPipe || outputMethod == TransportMethod.StandardOut)) { StreamLog.Trace(context.Identifier, "Transcoder running: {0}", !transcoderApplication.HasExited); StreamLog.Info(context.Identifier, "Encoding: Waiting till output named pipe is ready"); var pipe = (NamedPipe)DataOutputStream; var checkFailed = context != null && context.TranscodingInfo != null; while (!pipe.IsReady && !(checkFailed && context.TranscodingInfo.Failed)) { System.Threading.Thread.Sleep(100); } if (checkFailed && context.TranscodingInfo.Failed) { StreamLog.Warn(context.Identifier, "Encoding: Aborting wait because transcoder application failed and will never setup output named pipe."); return(false); } } return(true); }
protected virtual VLCParameters GenerateVLCParameters(string options, string tsOptions, bool disableSeeking, string encoderOptions, string tsEncoderOptions, string muxerOptions) { List <string> arguments = options.Split(' ').Where(x => x.Length > 0).ToList(); // input string inURL = ""; if (Context.NeedsInputReaderUnit) { inURL = @"stream://\#IN#"; } else { inURL = Context.Source.GetPath(); } // add tv options if specified if ((Context.IsTv || Context.MediaInfo.Container == "MPEG-TS") && tsOptions.Length > 0) { arguments.AddRange(tsOptions.Split(' ').Where(x => x.Length > 0)); } // position (disabling this is probably a bad idea as some things (watch sharing, transcoding info) fail then, which results in faulty clients.) if (Context.StartPosition > 0 && !disableSeeking) { arguments.Add("--start-time=" + (Context.StartPosition / 1000)); } // audio track if (Context.AudioTrackId != null) { arguments.Add("--audio-track=" + Context.MediaInfo.AudioStreams.Where(x => x.ID == Context.AudioTrackId).First().Index); } else { StreamLog.Warn(Identifier, "VLC streaming without audio track is not implemented yet"); } // subtitle selection string subtitleTranscoder; if (Context.SubtitleTrackId == null) { subtitleTranscoder = "scodec=none"; } else { WebSubtitleStream stream = Context.MediaInfo.SubtitleStreams.First(x => x.ID == Context.SubtitleTrackId); if (stream.Filename != null) { arguments.Add("--sub-file=" + stream.Filename); } else { arguments.Add("--sub-track=" + stream.Index); } subtitleTranscoder = "soverlay"; } // create parameters string sout; if (!String.IsNullOrEmpty(encoderOptions)) { sout = "#transcode{" + encoderOptions + "," + subtitleTranscoder; if ((Context.IsTv || Context.MediaInfo.Container == "MPEG-TS") && !String.IsNullOrEmpty(tsEncoderOptions)) { sout += "," + tsEncoderOptions; } if (!Context.Profile.TranscoderParameters.ContainsKey("noResize") || Context.Profile.TranscoderParameters["noResize"] != "yes") { sout += ",width=" + Context.OutputSize.Width + ",height=" + Context.OutputSize.Height; } sout += "}" + muxerOptions; } else { sout = "#" + muxerOptions.Substring(1); } // return return(new VLCParameters() { Sout = sout, Arguments = arguments.ToArray(), Input = inURL }); }
private void ParseOutputStream(Stream stdoutStream, Reference <WebTranscodingInfo> data, long startPosition, bool logProgress) { StreamReader reader = new StreamReader(stdoutStream); TranscodingInfoCalculator calculator = new TranscodingInfoCalculator(position, 25, 500, info.Duration); //VLCWrapper prints twice a second string line; try { while ((line = reader.ReadLine()) != null) { try { StreamLog.Trace(identifier, "VLCWrapperParsing: read line {0}", line); // just for debugging of the wrapper tool if (line.StartsWith("A") || line.StartsWith("I") || line == "S started" || line == "S null") { continue; } // propagate start event to Start() method if (line == "S playing") { vlcIsStarted = true; continue; } // events if (line == "S error") { vlcIsStarted = true; data.Value.Finished = true; data.Value.Failed = true; break; } if (line == "S finished") { data.Value.Finished = true; continue; } // the actual progress parsing if (line.StartsWith("P")) { // Starting with VLCWrapper 0.2, the output format has changed. It is 'P [time in milliseconds]' now, which is quite easy for // us to handle. With VLC 2 it also returns the time as a 64-bit integer, so we don't have overflow anymore either, but that risk // is with milliseconds quite small anyhow: it requires 596 hours of video. long milliseconds = Int64.Parse(line.Substring(2)); calculator.NewTime((int)milliseconds); calculator.SaveStats(data); continue; } StreamLog.Warn(identifier, "VLCWrapperParsing: encountered unknown line {0}", line); } catch (ThreadAbortException) { data.Value.Finished = true; reader.Close(); return; } catch (Exception e) { StreamLog.Error(identifier, "Failure during parsing of VLC output", e); } } } catch (ThreadAbortException) { // The double try-catch is to make sure that the parsing doesn't stop when it can't process a single line, but that we don't // log too much noise when a ThreadAbortException occurs while in the ReadLine() method. Funnily enough, this exception is // rethrown when we leave the catch block, so duplicate some code from below... data.Value.Finished = true; reader.Close(); return; } data.Value.Finished = true; reader.Close(); }