protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) { if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0) { Logger.Debug("Cannot stream copy video due to missing keyframe info"); return(false); } var previousSegment = 0; foreach (var frame in videoStream.KeyFrames) { var length = frame - previousSegment; // Don't allow really long segments because this could result in long download times if (length > 10000) { Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length); return(false); } previousSegment = frame; } return(base.CanStreamCopyVideo(request, videoStream)); }
/// <summary> /// Gets the video bitrate to specify on the command line /// </summary> /// <param name="request">The request.</param> /// <param name="videoCodec">The video codec.</param> /// <returns>System.String.</returns> private string GetVideoQualityParam(VideoStreamRequest request, string videoCodec) { var args = string.Empty; // webm if (videoCodec.Equals("libvpx", StringComparison.OrdinalIgnoreCase)) { args = "-quality realtime -profile:v 1 -slices 4"; } // asf/wmv else if (videoCodec.Equals("wmv2", StringComparison.OrdinalIgnoreCase)) { args = "-g 100 -qmax 15"; } else if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase)) { args = "-preset superfast"; } if (request.VideoBitRate.HasValue) { args += " -b:v " + request.VideoBitRate; } return(args.Trim()); }
public override bool Equals(object other) { if (!(other is VideoStreamRequest)) { return(false); } VideoStreamRequest vsr = other as VideoStreamRequest; if (Stream != vsr.Stream) { return(false); } if (Format != vsr.Format) { return(false); } if (Width != vsr.Width) { return(false); } if (Height != vsr.Height) { return(false); } if (Framerate != vsr.Framerate) { return(false); } if (StreamIndex != vsr.StreamIndex) { return(false); } return(true); }
public bool HasConflict(VideoStreamRequest other) { if (Stream != Stream.Any && Stream != other.Stream) { return(true); } if (Format != Format.Any && Format != other.Format) { return(true); } if (Width != 0 && Width != other.Width) { return(true); } if (Height != 0 && Height != other.Height) { return(true); } if (Framerate != 0 && Framerate != other.Framerate) { return(true); } if (StreamIndex != 0 && StreamIndex != other.StreamIndex) { return(true); } return(false); }
virtual protected void Awake() { threadId = Thread.CurrentThread.ManagedThreadId; _videoStreamFilter = new VideoStreamRequest() { Stream = _stream, Format = _format, StreamIndex = _streamIndex }; _currVideoStreamFilter = _videoStreamFilter.Clone(); }
private int GetStartNumber(VideoStreamRequest request) { var segmentId = "0"; if (request is GetHlsVideoSegment segmentRequest) { segmentId = segmentRequest.SegmentId; } return(int.Parse(segmentId, NumberStyles.Integer, CultureInfo.InvariantCulture)); }
private int GetStartNumber(VideoStreamRequest request) { var segmentId = "0"; var segmentRequest = request as GetHlsVideoSegment; if (segmentRequest != null) { segmentId = segmentRequest.SegmentId; } return int.Parse(segmentId, NumberStyles.Integer, UsCulture); }
private void ResetTexture(VideoStreamRequest vsr) { if (texture != null) { Destroy(texture); } texture = new Texture2D(vsr.Width, vsr.Height, Convert(vsr.Format), false, true) { wrapMode = TextureWrapMode.Clamp, filterMode = filterMode }; _currVideoStreamFilter = vsr.Clone(); texture.Apply(); textureBinding.Invoke(texture); }
private int GetStartNumber(VideoStreamRequest request) { var segmentId = "0"; var segmentRequest = request as GetDashSegment; if (segmentRequest != null) { segmentId = segmentRequest.SegmentId; } if (string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase)) { return(-1); } return(int.Parse(segmentId, NumberStyles.Integer, UsCulture)); }
void OnEnable() { m_pipeline = new Pipeline(); using (var cfg = DeviceConfiguration.ToPipelineConfig()) { ActiveProfile = m_pipeline.Start(cfg); } using (var activeStreams = ActiveProfile.Streams) { DeviceConfiguration.Profiles = new VideoStreamRequest[activeStreams.Count]; for (int i = 0; i < DeviceConfiguration.Profiles.Length; i++) { var s = activeStreams[i]; var p = new VideoStreamRequest() { Stream = s.Stream, Format = s.Format, Framerate = s.Framerate, StreamIndex = s.Index, }; var vs = s as VideoStreamProfile; if (vs != null) { p.Width = vs.Width; p.Height = vs.Height; } DeviceConfiguration.Profiles[i] = p; } } if (processMode == ProcessMode.Multithread) { stopEvent.Reset(); worker = new Thread(WaitForFrames); worker.IsBackground = true; worker.Start(); } StartCoroutine(WaitAndStart()); }
private async Task <object> GetPlaylistAsync(VideoStreamRequest request, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); var builder = new StringBuilder(); builder.AppendLine("#EXTM3U"); builder.AppendLine("#EXT-X-VERSION:3"); builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture)); builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); builder.AppendLine("#EXT-X-ALLOW-CACHE:NO"); var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds; var index = 0; while (seconds > 0) { var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds; builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ","); builder.AppendLine(string.Format("hlsdynamic/{0}/{1}.ts{2}", name, index.ToString(UsCulture), queryString)); seconds -= state.SegmentLength; index++; } builder.AppendLine("#EXT-X-ENDLIST"); var playlistText = builder.ToString(); return(ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary <string, string>())); }
private async Task<object> GetPlaylistAsync(VideoStreamRequest request, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); var builder = new StringBuilder(); builder.AppendLine("#EXTM3U"); builder.AppendLine("#EXT-X-VERSION:3"); builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture)); builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); builder.AppendLine("#EXT-X-ALLOW-CACHE:NO"); var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds; var index = 0; while (seconds > 0) { var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds; builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ","); builder.AppendLine(string.Format("hlsdynamic/{0}/{1}.ts{2}", name, index.ToString(UsCulture), queryString)); seconds -= state.SegmentLength; index++; } builder.AppendLine("#EXT-X-ENDLIST"); var playlistText = builder.ToString(); return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); }
private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId) { if ((request.StartTimeTicks ?? 0) > 0) { throw new ArgumentException("StartTimeTicks is not allowed."); } var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; var index = int.Parse(segmentId, NumberStyles.Integer, UsCulture); var state = await GetState(request, cancellationToken).ConfigureAwait(false); var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); var segmentPath = GetSegmentPath(playlistPath, index); var segmentLength = state.SegmentLength; TranscodingJob job = null; if (File.Exists(segmentPath)) { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls); return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false); } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { if (File.Exists(segmentPath)) { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls); return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false); } else { var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath); if (currentTranscodingIndex == null || index < currentTranscodingIndex.Value || (index - currentTranscodingIndex.Value) > 4) { // If the playlist doesn't already exist, startup ffmpeg try { await ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType.Hls && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase), false).ConfigureAwait(false); if (currentTranscodingIndex.HasValue) { DeleteLastFile(playlistPath, 0); } var startSeconds = index * state.SegmentLength; request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks; job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false); } catch { state.Dispose(); throw; } await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); } } } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } Logger.Info("waiting for {0}", segmentPath); while (!File.Exists(segmentPath)) { await Task.Delay(50, cancellationToken).ConfigureAwait(false); } Logger.Info("returning {0}", segmentPath); job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType.Hls); return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false); }
protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) { return(false); //return base.CanStreamCopyVideo(request, videoStream); }
private async Task <object> GetDynamicSegment(VideoStreamRequest request, string segmentId) { if ((request.StartTimeTicks ?? 0) > 0) { throw new ArgumentException("StartTimeTicks is not allowed."); } var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, UsCulture); var state = await GetState(request, cancellationToken).ConfigureAwait(false); var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); var segmentPath = GetSegmentPath(playlistPath, requestedIndex); var segmentLength = state.SegmentLength; var segmentExtension = GetSegmentFileExtension(state); TranscodingJob job = null; if (File.Exists(segmentPath)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false)); } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { if (File.Exists(segmentPath)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false)); } else { var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength; if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange) { // If the playlist doesn't already exist, startup ffmpeg try { ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId ?? request.ClientTime, p => false); if (currentTranscodingIndex.HasValue) { DeleteLastFile(playlistPath, segmentExtension, 0); } request.StartTimeTicks = GetSeekPositionTicks(state, requestedIndex); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false); } catch { state.Dispose(); throw; } await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); } } } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } Logger.Info("waiting for {0}", segmentPath); while (!File.Exists(segmentPath)) { await Task.Delay(50, cancellationToken).ConfigureAwait(false); } Logger.Info("returning {0}", segmentPath); job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false)); }
private int GetStartNumber(VideoStreamRequest request) { var segmentId = "0"; var segmentRequest = request as GetDashSegment; if (segmentRequest != null) { segmentId = segmentRequest.SegmentId; } if (string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase)) { return -1; } return int.Parse(segmentId, NumberStyles.Integer, UsCulture); }
protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs) { return false; }
private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId, string representationId) { if ((request.StartTimeTicks ?? 0) > 0) { throw new ArgumentException("StartTimeTicks is not allowed."); } var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; var requestedIndex = string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase) ? -1 : int.Parse(segmentId, NumberStyles.Integer, UsCulture); var state = await GetState(request, cancellationToken).ConfigureAwait(false); var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".mpd"); var segmentExtension = GetSegmentFileExtension(state); var segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex); var segmentLength = state.SegmentLength; TranscodingJob job = null; if (!string.IsNullOrWhiteSpace(segmentPath)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false); } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex); if (!string.IsNullOrWhiteSpace(segmentPath)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false); } else { if (string.Equals(representationId, "0", StringComparison.OrdinalIgnoreCase)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength; Logger.Debug("Current transcoding index is {0}. requestedIndex={1}. segmentGapRequiringTranscodingChange={2}", currentTranscodingIndex ?? -2, requestedIndex, segmentGapRequiringTranscodingChange); if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange) { // If the playlist doesn't already exist, startup ffmpeg try { ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false); if (currentTranscodingIndex.HasValue) { DeleteLastTranscodedFiles(playlistPath, 0); } var positionTicks = GetPositionTicks(state, requestedIndex); request.StartTimeTicks = positionTicks; var startNumber = GetStartNumber(state); var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture)); state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath)); Directory.CreateDirectory(workingDirectory); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false); await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false); } catch { state.Dispose(); throw; } } } } } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } while (string.IsNullOrWhiteSpace(segmentPath)) { segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex); await Task.Delay(50, cancellationToken).ConfigureAwait(false); } Logger.Info("returning {0}", segmentPath); return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType), cancellationToken).ConfigureAwait(false); }
protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List <string> supportedAudioCodecs) { return(false); }
private async Task <object> GetDynamicSegment(VideoStreamRequest request, string segmentId, string representationId) { if ((request.StartTimeTicks ?? 0) > 0) { throw new ArgumentException("StartTimeTicks is not allowed."); } var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; var requestedIndex = string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase) ? -1 : int.Parse(segmentId, NumberStyles.Integer, UsCulture); var state = await GetState(request, cancellationToken).ConfigureAwait(false); var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".mpd"); var segmentExtension = GetSegmentFileExtension(state); var segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex); var segmentLength = state.SegmentLength; TranscodingJob job = null; if (!string.IsNullOrWhiteSpace(segmentPath)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false)); } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex); if (!string.IsNullOrWhiteSpace(segmentPath)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false)); } else { if (string.Equals(representationId, "0", StringComparison.OrdinalIgnoreCase)) { job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength; Logger.Debug("Current transcoding index is {0}. requestedIndex={1}. segmentGapRequiringTranscodingChange={2}", currentTranscodingIndex ?? -2, requestedIndex, segmentGapRequiringTranscodingChange); if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange) { // If the playlist doesn't already exist, startup ffmpeg try { ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false); if (currentTranscodingIndex.HasValue) { DeleteLastTranscodedFiles(playlistPath, 0); } var positionTicks = GetPositionTicks(state, requestedIndex); request.StartTimeTicks = positionTicks; var startNumber = GetStartNumber(state); var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture)); state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath)); Directory.CreateDirectory(workingDirectory); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false); await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false); } catch { state.Dispose(); throw; } } } } } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } while (string.IsNullOrWhiteSpace(segmentPath)) { segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex); await Task.Delay(50, cancellationToken).ConfigureAwait(false); } Logger.Info("returning {0}", segmentPath); return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType), cancellationToken).ConfigureAwait(false)); }
protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) { return false; //return base.CanStreamCopyVideo(request, videoStream); }
protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) { if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0) { Logger.Debug("Cannot stream copy video due to missing keyframe info"); return false; } var previousSegment = 0; foreach (var frame in videoStream.KeyFrames) { var length = frame - previousSegment; // Don't allow really long segments because this could result in long download times if (length > 10000) { Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length); return false; } previousSegment = frame; } return base.CanStreamCopyVideo(request, videoStream); }
private async Task <object> GetDynamicSegment(VideoStreamRequest request, string segmentId) { if ((request.StartTimeTicks ?? 0) > 0) { throw new ArgumentException("StartTimeTicks is not allowed."); } var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; var index = int.Parse(segmentId, NumberStyles.Integer, UsCulture); var state = await GetState(request, cancellationToken).ConfigureAwait(false); var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); var segmentExtension = GetSegmentFileExtension(state); var segmentPath = GetSegmentPath(playlistPath, segmentExtension, index); var segmentLength = state.SegmentLength; TranscodingJob job = null; if (File.Exists(segmentPath)) { return(await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false)); } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { if (File.Exists(segmentPath)) { return(await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false)); } else { var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); if (currentTranscodingIndex == null || index < currentTranscodingIndex.Value || (index - currentTranscodingIndex.Value) > 4) { // If the playlist doesn't already exist, startup ffmpeg try { ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase)); if (currentTranscodingIndex.HasValue) { DeleteLastFile(playlistPath, segmentExtension, 0); } var startSeconds = index * state.SegmentLength; request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks; job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, Path.GetDirectoryName(playlistPath)).ConfigureAwait(false); } catch { state.Dispose(); throw; } await WaitForMinimumSegmentCount(playlistPath, 2, cancellationTokenSource.Token).ConfigureAwait(false); } } } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } Logger.Info("waiting for {0}", segmentPath); while (!File.Exists(segmentPath)) { await Task.Delay(50, cancellationToken).ConfigureAwait(false); } Logger.Info("returning {0}", segmentPath); job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType); return(await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false)); }