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 Stop() { StreamLog.Debug(context.Identifier, "VLCManagedEncoder: Stopping transcoding"); try { DataOutputStream.Close(); } catch (Exception e) { StreamLog.Info(context.Identifier, "VLCManagedEncoder: Failed to close data output stream", e); } inputTimer.Enabled = false; StreamLog.Trace(context.Identifier, "VLCManagedEncoder: Trying to stop vlc"); transcoder.StopTranscoding(); transcoder = null; StreamLog.Debug(context.Identifier, "VLCManagedEncoder: Stopped transcoding"); return(true); }
public bool Start() { // setup input if (inputMethod == InputMethod.NamedPipe) { ((NamedPipe)transcoderInputStream).WaitTillReady(); StreamLog.Info(context.Identifier, "VLCManagedEncoder: Copy stream of type {0} into transcoder input pipe", InputStream.ToString()); StreamCopy.AsyncStreamCopy(InputStream, transcoderInputStream, "transinput"); } // delay start of next unit till our output stream is ready StreamLog.Info(context.Identifier, "VLCManagedEncoder: Waiting till output named pipe is ready"); ((NamedPipe)DataOutputStream).WaitTillReady(); // TODO: wait for state machine // setup the data reading infoReference = new Reference <WebTranscodingInfo>(() => context.TranscodingInfo, x => { context.TranscodingInfo = x; }); if (context.MediaInfo.Duration > 0) { StreamLog.Trace(context.Identifier, "VLCManagedInfo: duration known; is {0}", context.MediaInfo.Duration); calculator = new TranscodingInfoCalculator(context.StartPosition, 25, POLL_DATA_TIME, context.MediaInfo.Duration); } else { StreamLog.Trace(context.Identifier, "VLCManagedInfo: duration unknown"); calculator = new TranscodingInfoCalculator(context.StartPosition, 25, POLL_DATA_TIME); } // and setup the timer inputTimer = new Timer() { AutoReset = true, Interval = POLL_DATA_TIME }; inputTimer.Elapsed += InfoTimerTick; inputTimer.Start(); return(true); }
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); }
private static void ParseOutputStream(Stream outputStream, Reference <WebTranscodingInfo> saveData, long startPosition, bool logMessages, bool logProgress, string identifier) { StreamReader reader = new StreamReader(outputStream); string line; while ((line = reader.ReadLine()) != null) { try { bool canBeErrorLine = true; if (line.StartsWith("frame=")) { // format of an output line (yes, we're doomed as soon as ffmpeg changes it output): // frame= 923 fps=256 q=31.0 size= 2712kB time=00:05:22.56 bitrate= 601.8kbits/s Match match = Regex.Match(line, @"frame=([ 0-9]*) fps=([ 0-9]*) q=[^ ]* L?size=([ 0-9]*)kB time=([0-9]{2}):([0-9]{2}):([0-9]{2})\.[0-9]{2} bitrate=([ .0-9]*)kbits/s", RegexOptions.IgnoreCase); if (match.Success) { canBeErrorLine = false; lock (saveData) { saveData.Value.TranscodedTime = (Int32.Parse(match.Groups[4].Value) * 3600 + Int32.Parse(match.Groups[5].Value) * 60 + Int32.Parse(match.Groups[6].Value)) * 1000; saveData.Value.TranscodedFrames = Int32.Parse(match.Groups[1].Value); saveData.Value.TranscodingPosition = startPosition + saveData.Value.TranscodedTime; saveData.Value.TranscodingFPS = Int32.Parse(match.Groups[2].Value); saveData.Value.OutputBitrate = (int)Math.Round(Decimal.Parse(match.Groups[7].Value, System.Globalization.CultureInfo.InvariantCulture)); } if (!logProgress) // we don't log output { continue; } } } if (line.StartsWith("video:")) { // process the result line to see if it completed successfully (example): // video:5608kB audio:781kB global headers:0kB muxing overhead 13.235302% Match resultMatch = Regex.Match(line, @"video:([0-9]*)kB audio:([0-9]*)kB global headers:([0-9]*)kB muxing overhead[^%]*%", RegexOptions.IgnoreCase); saveData.Value.Finished = true; canBeErrorLine = false; } // show error messages if (logMessages && canBeErrorLine) { StreamLog.Trace(identifier, "ffmpeg: " + line); } } catch (ThreadAbortException) { saveData.Value.Failed = true; saveData.Value.Finished = true; reader.Close(); return; } catch (Exception e) { StreamLog.Error(identifier, "Failure during parsing of ffmpeg output", e); } } saveData.Value.Finished = true; reader.Close(); return; }
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(); }