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)); }
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 WebStringResult StartStreamWithStreamSelection(string identifier, string profileName, long startPosition, int audioId, int subtitleId) { StreamLog.Debug(identifier, "Called StartStreamWithStreamSelection with profile={0}; start={1}; audioId={2}; subtitleId={3}", profileName, startPosition, audioId, subtitleId); _stream.EndStream(identifier); // first end previous stream, if any available return(_stream.StartStream(identifier, Configuration.StreamingProfiles.GetTranscoderProfileByName(profileName), startPosition * 1000, audioId, subtitleId)); }
public WebStreamLogs GetStreamLogs(string identifier) { var slh = StreamLog.GetStreamLogDetails(identifier); return(new WebStreamLogs() { LastError = slh.LastError, FullLogs = slh.FullLog.ToString() }); }
public void StreamLogStoresAndRetrievesLogEntries() { var serializer = CreateSerializer(); using (var memoryStream = new MemoryStream()) { var log = new StreamLog <TestOperation>(memoryStream, serializer); TestEmptyLog(log); TestLog(log); TestLog(log); // test again, as it will overwrite the existing entries } }
public void StreamLogStoresAndRetrievesLogEntries() { var serializer = CreateSerializer(); using (var memoryStream = new MemoryStream()) { var log = new StreamLog<TestOperation>(memoryStream, serializer); TestEmptyLog(log); TestLog(log); TestLog(log); // test again, as it will overwrite the existing entries } }
private void CloseStream(Stream stream, string logName) { try { if (stream != null) { stream.Close(); } } catch (Exception e) { StreamLog.Info(context.Identifier, "Encoding: Failed to close {0} stream: {1}", logName, e.Message); } }
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 Stream DoStream(WebMediaType type, int?provider, string itemId, string clientDescription, string profileName, long startPosition, int?idleTimeout) { if (!IsClientAuthorized()) { Log.Warn("Host {0} isn't authorized to call DoStream", WCFUtil.GetClientIPAddress()); WCFUtil.SetResponseCode(HttpStatusCode.Unauthorized); return(Stream.Null); } // calculate timeout, which is by default 5 minutes for direct streaming and 5 seconds for transcoded streams var profile = Configuration.StreamingProfiles.Transcoders.FirstOrDefault(x => x.Name == profileName); if (profile == null) { Log.Warn("Called DoStream with non-existing profile {0}", profileName); return(Stream.Null); } int timeout = profile.Transcoder == typeof(Transcoders.Direct).FullName ? 5 * 60 : 5; if (idleTimeout.HasValue) { timeout = idleTimeout.Value; } // This only works with profiles that actually return something in the RetrieveStream method (i.e. no RTSP or CustomTranscoderData) string identifier = String.Format("dostream-{0}", new Random().Next(10000, 99999)); StreamLog.Debug(identifier, "DoStream: using timeout={0}", timeout); if (!InitStream(type, provider, itemId, null, clientDescription, identifier, timeout)) { StreamLog.Info(identifier, "DoStream: InitStream() failed"); FinishStream(identifier); return(Stream.Null); } if (String.IsNullOrEmpty(StartStream(identifier, profileName, startPosition))) { StreamLog.Info(identifier, "DoStream: StartStream failed"); FinishStream(identifier); return(Stream.Null); } StreamLog.Info(identifier, "DoStream: succeeded, returning stream"); return(RetrieveStream(identifier)); }
public WebBoolResult FinishStream(string identifier) { StreamLog.Debug(identifier, "Called FinishStream"); _stream.KillStream(identifier); lock (_timeshiftings) { if (_timeshiftings.ContainsKey(identifier) && _timeshiftings[identifier] != null) { StreamLog.Info(identifier, "Cancelling timeshifting"); Connections.TAS.CancelCurrentTimeShifting("mpextended-" + identifier); _timeshiftings.Remove(identifier); } } return(true); }
private void StreamLogCanOpenAnExistingStream() { var serializer = CreateSerializer(); using (var memoryStream = new MemoryStream()) { var log1 = new StreamLog <TestOperation>(memoryStream, serializer); TestLog(log1); var log2 = new StreamLog <TestOperation>(memoryStream, serializer); var entries = log2.GetCursor(1).ToArray(); entries.Should().HaveCount(3); entries[0].Id.Should().Be(new LogEntryId(1, 1)); entries[1].Id.Should().Be(new LogEntryId(1, 2)); entries[2].Id.Should().Be(new LogEntryId(1, 3)); } }
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 Setup() { // setup output named pipe DataOutputStream = new NamedPipe(); string output = ((NamedPipe)DataOutputStream).Url; StreamLog.Info(context.Identifier, "VLCManagedEncoder: starting output named pipe {0}", output); ((NamedPipe)DataOutputStream).Start(false); // prepare sout, needs some trickery for vlc string realSout = sout.Replace("#OUT#", @"\" + output); // debug StreamLog.Debug(context.Identifier, "VLCManagedEncoder: sout {0}", realSout); StreamLog.Debug(context.Identifier, "VLCManagedEncoder: arguments {0}", String.Join("|", arguments)); // start vlc transcoder = new VLCTranscoder(); transcoder.SetArguments(arguments); transcoder.SetMediaName(Guid.NewGuid().ToString()); transcoder.SetSout(realSout); // setup input if (inputMethod == InputMethod.NamedPipe) { transcoderInputStream = new NamedPipe(); StreamLog.Info(context.Identifier, "VLCManagedEncoder: starting input named pipe {0}", transcoderInputStream); ((NamedPipe)transcoderInputStream).Start(false); inputPath = "stream://" + transcoderInputStream.Url; // use special syntax for VLC to pick up named pipe } StreamLog.Debug(context.Identifier, "VLCManagedEncoder: input {0}", inputPath); transcoder.SetInput(inputPath); // start transcoding transcoder.StartTranscoding(); // doesn't work //transcoder.Seek(startPos * 1.0 / context.MediaInfo.Duration * 1000); context.TranscodingInfo.Supported = true; return(true); }
public void DeleteStream(int i) { StreamLog stream = this.streams[i]; stream.Uninit(); stream.FilterAltered -= this.console.SaveModules; stream.OptionAltered -= this.console.SaveModules; this.streams.RemoveAt(i); if (this.StreamDeleted != null) { this.StreamDeleted(stream); } foreach (Vars var in this.perWindowVars.Each()) { var.workingStream = Mathf.Clamp(var.workingStream, 0, this.streams.Count - 1); } this.console.SaveModules(); }
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 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 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 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); }
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); }
public void CouldAddNextChunkBeforeCompleted() { var path = TestUtils.GetPath(); var processConfig = new ProcessConfig(path); StartupConfig.StreamLogBufferPoolFlags = LMDBEnvironmentFlags.NoSync; StartupConfig.StreamBlockIndexFlags = LMDBEnvironmentFlags.NoSync; var slm = new StreamLogManager(processConfig, "CouldAddNextChunk", null, 512, true, true); var bufferPool = slm.BufferPool; var blockIndex = slm.BlockIndex; var streamId = (StreamLogId)42; short valueSize = 8; var state = slm.StateStorage.GetState(streamId); state.CheckInit(streamId, 8, SerializationFormat.Binary); var sl = new StreamLog(slm, state, textId: "test_stream"); var count = 100; SharedMemory currentBuffer = null; using (Benchmark.Run("AddNext (KOPS)", count * 1000)) { var version = 1UL; var block = blockIndex.RentNextWritableBlock(sl, minimumLength: 1, version, default); // var slc = new StreamBlock(buffer.RetainChunkMemory(false), streamId, valueSize, 1); var firstBufferRef = block.SharedMemory.BufferRef; for (int i = 0; i < count; i++) { version = (ulong)i + 1; if (!block.Claim(version, 8).IsValid) { Assert.Fail("!slc.Claim(version, 8).IsValid"); } block.Commit(); var bufferRef = block.SharedMemory.BufferRef; blockIndex.PrepareNextWritableStreamBlock(sl, length: 1); blockIndex.PrepareNextWritableStreamBlock(sl, length: 1); block.Complete(); block = blockIndex.RentNextWritableBlock(sl, minimumLength: 1, version + 1, default); var bufferRef1 = block.SharedMemory.BufferRef; // As if someone has created the next buffer before the second call, forget the first call block.DisposeFree(); block = blockIndex.RentNextWritableBlock(sl, minimumLength: 1, version + 1, default); var bufferRef2 = block.SharedMemory.BufferRef; if (block.SharedMemory.ReferenceCount != 2) { Assert.Fail($"buffer.RefCount {block.SharedMemory.ReferenceCount} != 1"); } if (bufferRef1 != bufferRef2) { Assert.Fail($"bufferRef1 {bufferRef1} != bufferRef2 {bufferRef2}"); } block.Complete(); var nextVersion = block.NextVersion; block.DisposeFree(); blockIndex.PrepareNextWritableStreamBlock(sl, length: 1); blockIndex.PrepareNextWritableStreamBlock(sl, length: 1); // block = new StreamBlock(block.SharedMemory.RetainBlockMemory(false), streamId, valueSize, nextVersion); block = blockIndex.RentNextWritableBlock(sl, minimumLength: 1, nextVersion, default); } if (!block.Claim(version + 1, 8).IsValid) { Assert.Fail("!slc.Claim(version, 8).IsValid"); } block.Commit(); block.Complete(); block = blockIndex.RentNextWritableBlock(sl, minimumLength: 1, block.NextVersion, default); Assert.AreEqual(2, block.SharedMemory.ReferenceCount); block.DisposeFree(); block = blockIndex.RentNextWritableBlock(sl, minimumLength: 1, version + 2, default); Assert.IsFalse(block.IsCompleted); // TODO test replace //// replace existing //var toReplaceVersion = slc.FirstVersion; //var toReplaceRef = buffer.BufferRef; //slc.Complete(); //// SharedMemory.Free(buffer); //// slc.DisposeFree(); //buffer = chunkIndex.GetOrCreateNextWritableChunkBuffer(sl, toReplaceVersion, toReplaceRef, 1); //slc = new StreamLogChunk(buffer.RetainChunkMemory(false), streamId, valueSize, version + 2); //Assert.AreNotEqual(toReplaceRef, buffer.BufferRef); // SharedMemory.Free(buffer); block.DisposeFree(); } //bufferPool.PrintBuffers(); //bufferPool.PrintBuffersAfterPoolDispose = true; Benchmark.Dump(); Console.WriteLine("Finished"); sl.Dispose(); slm.Dispose(); }
public bool Setup() { // sets up streams string input = ""; string output = ""; bool needsStdin = false; bool needsStdout = false; // input if (inputMethod == TransportMethod.NamedPipe) { transcoderInputStream = new NamedPipe(); input = ((NamedPipe)transcoderInputStream).Url; StreamLog.Info(context.Identifier, "Encoding: starting input named pipe {0}", input); ((NamedPipe)transcoderInputStream).Start(false); doInputCopy = true; } else if (inputMethod == TransportMethod.StandardIn) { needsStdin = true; doInputCopy = true; } // output stream if (outputMethod == TransportMethod.NamedPipe) { DataOutputStream = new NamedPipe(); output = ((NamedPipe)DataOutputStream).Url; StreamLog.Info(context.Identifier, "Encoding: starting output named pipe {0}", output); ((NamedPipe)DataOutputStream).Start(false); } else if (outputMethod == TransportMethod.StandardOut) { needsStdout = true; } // arguments substitution arguments = arguments.Replace("#IN#", input).Replace("#OUT#", output); // start transcoder if (!SpawnTranscoder(needsStdin, needsStdout)) { return(false); } // finish stream setup if (inputMethod == TransportMethod.StandardIn) { transcoderInputStream = transcoderApplication.StandardInput.BaseStream; } if (outputMethod == TransportMethod.StandardOut) { DataOutputStream = transcoderApplication.StandardOutput.BaseStream; } // setup log stream if (!DebugOutput && logStream == LogStream.StandardOut && IsLogStreamConnected && outputMethod != TransportMethod.StandardOut) { LogOutputStream = transcoderApplication.StandardOutput.BaseStream; } else if (!DebugOutput && logStream == LogStream.StandardError && IsLogStreamConnected) { LogOutputStream = transcoderApplication.StandardError.BaseStream; } else { LogOutputStream = new MemoryStream(4096); } return(true); }
void StreamLogCanOpenAnExistingStream() { var serializer = CreateSerializer(); using (var memoryStream = new MemoryStream()) { var log1 = new StreamLog<TestOperation>(memoryStream, serializer); TestLog(log1); var log2 = new StreamLog<TestOperation>(memoryStream, serializer); var entries = log2.GetCursor(1).ToArray(); entries.Should().HaveCount(3); entries[0].Id.Should().Be(new LogEntryId(1, 1)); entries[1].Id.Should().Be(new LogEntryId(1, 2)); entries[2].Id.Should().Be(new LogEntryId(1, 3)); } }
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(); }
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 Rect DrawStreamTabs(Rect r) { ConsoleSettings settings = HQ.Settings.Get <ConsoleSettings>(); float maxWidth = r.width; r.height = Constants.SingleLineHeight; // Switch stream if (settings.inputsManager.Check("Navigation", ConsoleConstants.SwitchNextStreamCommand) == true) { this.currentVars.workingStream += 1; if (this.currentVars.workingStream >= this.streams.Count) { this.currentVars.workingStream = 0; } Event.current.Use(); } if (settings.inputsManager.Check("Navigation", ConsoleConstants.SwitchPreviousStreamCommand) == true) { this.currentVars.workingStream -= 1; if (this.currentVars.workingStream < 0) { this.currentVars.workingStream = this.streams.Count - 1; } Event.current.Use(); } for (int i = 0; i < this.streams.Count; i++) { r = this.streams[i].OnTabGUI(r, i); } r.width = 16F; if (GUI.Button(r, "+", HQ.Settings.Get <GeneralSettings>().MenuButtonStyle) == true) { RemoteModuleSettings remoteSettings = HQ.Settings.Get <RemoteModuleSettings>(); StreamLog stream = new StreamLog(); stream.Init(this.console, this); stream.rowsDrawer.SetRowGetter(this); stream.FilterAltered += this.console.SaveModules; stream.OptionAltered += this.console.SaveModules; foreach (ILogFilter filter in remoteSettings.GenerateFilters()) { stream.groupFilters.filters.Add(filter); } this.streams.Add(stream); if (this.StreamAdded != null) { this.StreamAdded(stream); } this.console.CheckNewLogConsume -= stream.ConsumeLog; this.console.PropagateNewLog -= stream.AddLog; this.console.SaveModules(); if (this.streams.Count == 1) { this.currentVars.workingStream = 0; } } r.x += r.width; if (this.streams.Count > 2) { r.y += r.height + 2F; r.x = 0F; r.width = maxWidth; } else { r.width = maxWidth - r.x; } return(r); }
public WebStringResult StartStream(string identifier, string profileName, long startPosition) { StreamLog.Debug(identifier, "Called StartStream with profile={0}; start={1}", profileName, startPosition); _stream.EndStream(identifier); // first end previous stream, if any available return(_stream.StartStream(identifier, Configuration.StreamingProfiles.GetTranscoderProfileByName(profileName), startPosition * 1000)); }
public WebBoolResult StopStream(string identifier) { StreamLog.Debug(identifier, "Called StopStream"); _stream.EndStream(identifier); 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; }
public static void Close() { StreamLog.Close(); }