public object Get(GetSubtitlePlaylist request) { var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); var mediaSource = _mediaSourceManager.GetStaticMediaSource(item, request.MediaSourceId, false); var builder = new StringBuilder(); var runtime = mediaSource.RunTimeTicks ?? -1; if (runtime <= 0) { throw new ArgumentException("HLS Subtitles are not supported for this media."); } builder.AppendLine("#EXTM3U"); builder.AppendLine("#EXT-X-TARGETDURATION:" + request.SegmentLength.ToString(CultureInfo.InvariantCulture)); builder.AppendLine("#EXT-X-VERSION:3"); builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); long positionTicks = 0; var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks; while (positionTicks < runtime) { var remaining = runtime - positionTicks; var lengthTicks = Math.Min(remaining, segmentLengthTicks); builder.AppendLine("#EXTINF:" + TimeSpan.FromTicks(lengthTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture)); var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks); var url = string.Format("stream.srt?StartPositionTicks={0}&EndPositionTicks={1}", positionTicks.ToString(CultureInfo.InvariantCulture), endPositionTicks.ToString(CultureInfo.InvariantCulture)); builder.AppendLine(url); positionTicks += segmentLengthTicks; } builder.AppendLine("#EXT-X-ENDLIST"); return(ResultFactory.GetResult(builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary <string, string>())); }
public static StreamParams ParseFromUrl(string url, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager) { var request = new StreamParams { ItemId = GetItemId(url) }; Guid parsedId; if (string.IsNullOrWhiteSpace(request.ItemId) || !Guid.TryParse(request.ItemId, out parsedId)) { return(request); } const string srch = "params="; var index = url.IndexOf(srch, StringComparison.OrdinalIgnoreCase); if (index == -1) { return(request); } var vals = url.Substring(index + srch.Length).Split(';'); for (var i = 0; i < vals.Length; i++) { var val = vals[i]; if (string.IsNullOrWhiteSpace(val)) { continue; } if (i == 0) { request.DeviceProfileId = val; } else if (i == 1) { request.DeviceId = val; } else if (i == 2) { request.MediaSourceId = val; } else if (i == 3) { request.IsDirectStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } else if (i == 6) { request.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture); } else if (i == 7) { request.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture); } else if (i == 14) { request.StartPositionTicks = long.Parse(val, CultureInfo.InvariantCulture); } } request.Item = string.IsNullOrWhiteSpace(request.ItemId) ? null : libraryManager.GetItemById(parsedId); var hasMediaSources = request.Item as IHasMediaSources; request.MediaSource = hasMediaSources == null ? null : mediaSourceManager.GetStaticMediaSource(hasMediaSources, request.MediaSourceId, false); return(request); }
public async Task <EncodingJob> CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress <double> progress, CancellationToken cancellationToken) { var request = options; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); } var state = new EncodingJob(_logger, _liveTvManager) { Options = options, IsVideoRequest = isVideoRequest, Progress = progress }; if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } var item = _libraryManager.GetItemById(request.ItemId); List <MediaStream> mediaStreams = null; state.ItemType = item.GetType().Name; if (item is ILiveTvRecording) { var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); var path = recording.RecordingInfo.Path; var mediaUrl = recording.RecordingInfo.Url; var source = string.IsNullOrEmpty(request.MediaSourceId) ? recording.GetMediaSources(false).First() : _mediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false); mediaStreams = source.MediaStreams; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; if (!string.IsNullOrEmpty(path)) { state.MediaPath = path; state.InputProtocol = MediaProtocol.File; } else if (!string.IsNullOrEmpty(mediaUrl)) { state.MediaPath = mediaUrl; state.InputProtocol = MediaProtocol.Http; } state.RunTimeTicks = recording.RunTimeTicks; state.DeInterlace = true; state.OutputAudioSync = "1000"; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; state.InputContainer = recording.Container; state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate; } else if (item is LiveTvChannel) { var channel = _liveTvManager.GetInternalChannel(request.ItemId); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); mediaStreams = new List <MediaStream>(); state.DeInterlace = true; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; } else { var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); mediaStreams = mediaSource.MediaStreams; state.MediaPath = mediaSource.Path; state.InputProtocol = mediaSource.Protocol; state.InputContainer = mediaSource.Container; state.InputFileSize = mediaSource.Size; state.InputBitrate = mediaSource.Bitrate; state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; state.RunTimeTicks = mediaSource.RunTimeTicks; state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; var video = item as Video; if (video != null) { state.IsInputVideo = true; if (mediaSource.VideoType.HasValue) { state.VideoType = mediaSource.VideoType.Value; } state.IsoType = mediaSource.IsoType; state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); if (mediaSource.Timestamp.HasValue) { state.InputTimestamp = mediaSource.Timestamp.Value; } } state.RunTimeTicks = mediaSource.RunTimeTicks; } AttachMediaStreamInfo(state, mediaStreams, request); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = GetAudioCodec(request); state.OutputAudioChannels = GetNumAudioChannelsParam(request, state.AudioStream, state.OutputAudioCodec); if (isVideoRequest) { state.OutputVideoCodec = GetVideoCodec(request); state.OutputVideoBitrate = GetVideoBitrateParamValue(request, state.VideoStream); if (state.OutputVideoBitrate.HasValue) { var resolution = ResolutionNormalizer.Normalize(state.OutputVideoBitrate.Value, state.OutputVideoCodec, request.MaxWidth, request.MaxHeight); request.MaxWidth = resolution.MaxWidth; request.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); if (isVideoRequest) { if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream)) { state.OutputVideoCodec = "copy"; } if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs)) { state.OutputAudioCodec = "copy"; } } return(state); }