public WebBoolResult InitStream(WebMediaType type, int?provider, string itemId, int?offset, string clientDescription, string identifier, int?idleTimeout) { AuthorizeStreaming(); if (type == WebMediaType.TV) { int channelId = Int32.Parse(itemId); lock (_timeshiftings) { StreamLog.Info(identifier, "Starting timeshifting on channel {0} for client {1}", channelId, clientDescription); var card = Connections.TAS.SwitchTVServerToChannelAndGetVirtualCard("mpextended-" + identifier, channelId); if (card == null) { StreamLog.Error(identifier, "Failed to start timeshifting for stream"); return(false); } else { StreamLog.Debug(identifier, "Timeshifting started!"); _timeshiftings[identifier] = card; itemId = card.TimeShiftFileName; } } } StreamLog.Info(identifier, "Called InitStream with type={0}; provider={1}; itemId={2}; offset={3}; clientDescription={4}; idleTimeout={5}", type, provider, itemId, offset, clientDescription, idleTimeout); return(_stream.InitStream(identifier, clientDescription, new MediaSource(type, provider, itemId, offset), idleTimeout.HasValue ? idleTimeout.Value : 5 * 60)); }
public bool Setup() { try { using (var context = NetworkContextFactory.CreateImpersonationContext()) { DataOutputStream = new FileStream(context.RewritePath(source), FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } } catch (Exception e) { StreamLog.Error(identifier, "Failed to setup ImpersonationInputUnit", e); return(false); } return(true); }
public bool Setup() { try { if (source.IndexOf(".ts.tsbuffer") != -1) { StreamLog.Info(identifier, "Using TsBuffer to read input"); DataOutputStream = new TsBuffer(this.source); } else { StreamLog.Info(identifier, "Using FileStream to read input"); DataOutputStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } } catch (Exception e) { StreamLog.Error(identifier, "Failed to setup InputProcessingUnit", e); return(false); } return(true); }
public bool Setup() { data.Value.Failed = false; vlcIsStarted = false; processThread = new Thread(delegate() { try { ParseOutputStream(InputStream, data, position, false); } catch (ThreadAbortException) { } catch (Exception ex) { StreamLog.Error(identifier, "VLCLogParsing failed with exception", ex); } }); processThread.Name = "VLCLogParsing"; processThread.Start(); return(true); }
public bool Stop() { // close streams CloseStream(InputStream, "input"); CloseStream(transcoderInputStream, "transcoder input"); CloseStream(DataOutputStream, "transcoder output"); try { if (transcoderApplication != null && !transcoderApplication.HasExited) { StreamLog.Debug(context.Identifier, "Encoding: Killing transcoder"); transcoderApplication.Stop(); } } catch (Exception e) { StreamLog.Error(context.Identifier, "Encoding: Failed to kill transcoder", e); } return(true); }
public bool Setup() { // this might be better placed in the Start() method, but EncoderUnit.Start() depends on this data.Value.Supported = true; data.Value.Failed = false; processThread = new Thread(delegate() { try { ParseOutputStream(InputStream, data, startPosition, LogMessages, LogProgress, identifier); } catch (ThreadAbortException) { // ThreadAbortException is already handled in ParseOutputStream, but rethrown when the method is left. Don't be noisy. } catch (Exception ex) { StreamLog.Error(identifier, "FFMpegLogParsing brutally came to an end", ex); } }); processThread.Name = "FFMpegLog"; processThread.Start(); return(true); }
private bool SpawnTranscoder(bool needsStdin, bool needsStdout) { ProcessStartInfo start = new ProcessStartInfo(transcoderPath, arguments); start.UseShellExecute = false; start.RedirectStandardInput = needsStdin; start.RedirectStandardOutput = needsStdout || (!DebugOutput && logStream == LogStream.StandardOut && IsLogStreamConnected); start.RedirectStandardError = !DebugOutput && logStream == LogStream.StandardError && IsLogStreamConnected; start.WindowStyle = DebugOutput ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden; start.CreateNoWindow = !DebugOutput; StreamLog.Info(context.Identifier, "Encoder: Transcoder configuration dump"); StreamLog.Info(context.Identifier, "Encoder: hasStdin {0}, hasStdout {1}, hasStderr {2}", start.RedirectStandardInput, start.RedirectStandardOutput, start.RedirectStandardError); StreamLog.Info(context.Identifier, "Encoder: path {0}", transcoderPath); StreamLog.Info(context.Identifier, "Encoder: arguments {0}", arguments); try { transcoderApplication = new TranscoderProcess(); transcoderApplication.StartInfo = start; if (context.Source.NeedsImpersonation) { transcoderApplication.StartAsUser(Configuration.Services.NetworkImpersonation.Domain, Configuration.Services.NetworkImpersonation.Username, Configuration.Services.NetworkImpersonation.GetPassword()); } else { transcoderApplication.Start(); } } catch (Win32Exception e) { StreamLog.Error(context.Identifier, "Encoding: Failed to start transcoder", e); 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(); }